protected FXForwardSecurity createFXForwardSecurity(final Bundle bundle) {
   final double putAmount = bundle._firstCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
   final ZonedDateTime forwardDate =
       nextWorkingDay(
           bundle._tradeDate.plusDays(bundle._daysOffset),
           bundle._firstCurrency,
           bundle._secondCurrency);
   final Double fxRate =
       getApproxFXRate(
           forwardDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
   if (fxRate == null) {
     return null;
   }
   final double callAmount = NOTIONAL * fxRate;
   final Currency payCurrency = bundle._long ? bundle._secondCurrency : bundle._firstCurrency;
   final Currency receiveCurrency = bundle._long ? bundle._firstCurrency : bundle._secondCurrency;
   final String dateString = forwardDate.toString(DATE_FORMATTER);
   final FXForwardSecurity fxForwardSecurity =
       new FXForwardSecurity(
           payCurrency, callAmount, receiveCurrency, putAmount, forwardDate, REGION);
   final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
   final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
   fxForwardSecurity.setName(
       "Pay "
           + payCurrency
           + " "
           + callAmountString
           + ", receive "
           + receiveCurrency
           + " "
           + putAmountString
           + " on "
           + dateString);
   return fxForwardSecurity;
 }
 protected FXBarrierOptionSecurity createFXBarrierOptionSecurity(final Bundle bundle) {
   final Currency putCurrency = bundle._firstCurrency;
   final Currency callCurrency = bundle._secondCurrency;
   final double putAmount = putCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
   final ZonedDateTime settlementDate =
       nextWorkingDay(
           bundle._tradeDate.plusDays(bundle._daysOffset),
           bundle._firstCurrency,
           bundle._secondCurrency);
   final Expiry expiry = new Expiry(settlementDate, ExpiryAccuracy.DAY_MONTH_YEAR);
   final Double fxRate =
       getApproxFXRate(
           settlementDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
   if (fxRate == null) {
     return null;
   }
   final double callAmount = NOTIONAL * fxRate;
   final String dateString = settlementDate.toString(DATE_FORMATTER);
   final BarrierType barrierType = bundle._up ? BarrierType.UP : BarrierType.DOWN;
   final BarrierDirection barrierDirection = BarrierDirection.KNOCK_IN;
   final MonitoringType monitoringType = MonitoringType.CONTINUOUS;
   final SamplingFrequency samplingFrequency = SamplingFrequency.DAILY_CLOSE;
   final double barrierLevel = bundle._up ? fxRate * 1.5 : fxRate / 1.5;
   final FXBarrierOptionSecurity fxBarrierOptionSecurity =
       new FXBarrierOptionSecurity(
           putCurrency,
           callCurrency,
           putAmount,
           callAmount,
           expiry,
           settlementDate,
           barrierType,
           barrierDirection,
           monitoringType,
           samplingFrequency,
           barrierLevel,
           bundle._long);
   final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
   final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
   final String barrierLevelString = RATE_FORMATTER.format(barrierLevel);
   final String barrierUnitString = callCurrency + "/" + putCurrency;
   fxBarrierOptionSecurity.setName(
       (bundle._long ? "Long " : "Short ")
           + (bundle._up ? "up" : "down")
           + " knock-in at "
           + barrierLevelString
           + " "
           + barrierUnitString
           + ", put "
           + putCurrency
           + " "
           + putAmountString
           + ", call "
           + callCurrency
           + " "
           + callAmountString
           + " on "
           + dateString);
   return fxBarrierOptionSecurity;
 }
 protected FXDigitalOptionSecurity createFXDigitalOptionSecurity(final Bundle bundle) {
   final Currency putCurrency = bundle._firstCurrency;
   final Currency callCurrency = bundle._secondCurrency;
   final Currency paymentCurrency = bundle._paymentCurrency;
   final double putAmount = putCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
   final ZonedDateTime expiry =
       nextWorkingDay(bundle._tradeDate.plusDays(bundle._daysOffset), putCurrency, callCurrency);
   final Double rate = getApproxFXRate(expiry.toLocalDate(), Pair.of(putCurrency, callCurrency));
   if (rate == null) {
     return null;
   }
   final double callAmount = rate * NOTIONAL;
   final ZonedDateTime settlementDate =
       nextWorkingDay(expiry.plusDays(2), putCurrency, callCurrency);
   final FXDigitalOptionSecurity security =
       new FXDigitalOptionSecurity(
           putCurrency,
           callCurrency,
           putAmount,
           callAmount,
           paymentCurrency,
           new Expiry(expiry),
           settlementDate,
           bundle._long);
   final StringBuilder sb = new StringBuilder("Digital ");
   sb.append(bundle._long ? "Long" : "Short");
   sb.append(" put ").append(putCurrency).append(' ').append(NOTIONAL_FORMATTER.format(putAmount));
   sb.append(", call ")
       .append(callCurrency)
       .append(' ')
       .append(NOTIONAL_FORMATTER.format(callAmount));
   sb.append(" on ").append(expiry.toString(DATE_FORMATTER));
   security.setName(sb.toString());
   return security;
 }
Example #4
0
 /**
  * Creates the output root data.
  *
  * @return the output root data, not null
  */
 protected FlexiBean createRootData() {
   FlexiBean out = super.createRootData();
   HolidayDocument latestDoc = data().getHoliday();
   HolidayDocument versionedHoliday = data().getVersioned();
   out.put("latestHolidayDoc", latestDoc);
   out.put("latestHoliday", latestDoc.getHoliday());
   out.put("holidayDoc", versionedHoliday);
   out.put("holiday", versionedHoliday.getHoliday());
   out.put("deleted", !latestDoc.isLatest());
   List<Pair<Year, List<LocalDate>>> map = new ArrayList<Pair<Year, List<LocalDate>>>();
   List<LocalDate> dates = versionedHoliday.getHoliday().getHolidayDates();
   if (dates.size() > 0) {
     int year = dates.get(0).getYear();
     int start = 0;
     int pos = 0;
     for (; pos < dates.size(); pos++) {
       if (dates.get(pos).getYear() == year) {
         continue;
       }
       map.add(Pair.of(Year.of(year), dates.subList(start, pos)));
       year = dates.get(pos).getYear();
       start = pos;
     }
     map.add(Pair.of(Year.of(year), dates.subList(start, pos)));
   }
   out.put("holidayDatesByYear", map);
   return out;
 }
Example #5
0
 private static void populateVolatilitySurfaceDefinitions(
     final ConfigMaster configMaster, final UniqueIdentifiable target) {
   final Tenor[] expiryTenors =
       new Tenor[] {
         Tenor.ofDays(7),
         Tenor.ofDays(14),
         Tenor.ofDays(21),
         Tenor.ofMonths(1),
         Tenor.ofMonths(3),
         Tenor.ofMonths(6),
         Tenor.ofMonths(9),
         Tenor.ofYears(1),
         Tenor.ofYears(5),
         Tenor.ofYears(10)
       };
   @SuppressWarnings("unchecked")
   final Pair<Number, FXVolQuoteType>[] deltaAndTypes =
       new Pair[] {
         Pair.of(25, FXVolQuoteType.BUTTERFLY),
         Pair.of(25, FXVolQuoteType.RISK_REVERSAL),
         Pair.of(15, FXVolQuoteType.BUTTERFLY),
         Pair.of(15, FXVolQuoteType.RISK_REVERSAL),
         Pair.of(0, FXVolQuoteType.ATM)
       };
   final VolatilitySurfaceDefinition<Tenor, Pair<Number, FXVolQuoteType>> volSurfaceDefinition =
       new VolatilitySurfaceDefinition<Tenor, Pair<Number, FXVolQuoteType>>(
           "SECONDARY_EURUSD_" + InstrumentTypeProperties.FOREX,
           target,
           expiryTenors,
           deltaAndTypes);
   ConfigMasterUtils.storeByName(configMaster, makeConfigDocument(volSurfaceDefinition));
 }
Example #6
0
  @SuppressWarnings("unchecked")
  @Test
  public void expandedRagged() {
    Tenor[] xs =
        new Tenor[] {Tenor.DAY, Tenor.ONE_WEEK, Tenor.TWO_WEEKS, Tenor.TWO_WEEKS, Tenor.ONE_MONTH};
    Tenor[] ys =
        new Tenor[] {
          Tenor.ONE_YEAR, Tenor.ONE_YEAR, Tenor.ONE_YEAR, Tenor.TWO_YEARS, Tenor.TWO_YEARS
        };
    double[] vols = new double[] {10, 11, 12, 13, 14};
    Map<Pair<Tenor, Tenor>, Double> values = Maps.newHashMap();
    for (int i = 0; i < xs.length; i++) {
      values.put(Pair.of(xs[i], ys[i]), vols[i]);
    }
    String name = "test";
    UniqueIdentifiable target = Currency.USD;
    VolatilitySurfaceData<Tenor, Tenor> data =
        new VolatilitySurfaceData<>(name, name, target, xs, ys, values);

    Map<String, Object> map =
        (Map<String, Object>)
            new VolatilitySurfaceDataFormatter()
                .format(data, null, TypeFormatter.Format.EXPANDED, null);
    assertEquals(
        Lists.newArrayList("1D", "7D", "14D", "1M"), map.get(SurfaceFormatterUtils.X_LABELS));
    assertEquals(Lists.newArrayList("1Y", "2Y"), map.get(SurfaceFormatterUtils.Y_LABELS));
    assertEquals(
        Lists.newArrayList(10d, 11d, 12d, null, null, null, 13d, 14d),
        map.get(SurfaceFormatterUtils.VOL));
  }
 @Override
 public synchronized YieldCurveDefinitionDocument update(YieldCurveDefinitionDocument document) {
   ArgumentChecker.notNull(document, "document");
   ArgumentChecker.notNull(document.getYieldCurveDefinition(), "document.yieldCurveDefinition");
   final Currency currency = document.getYieldCurveDefinition().getCurrency();
   final String name = document.getYieldCurveDefinition().getName();
   final UniqueId uid = UniqueId.of(getUniqueIdScheme(), name + "_" + currency.getCode());
   if (!uid.equals(document.getUniqueId())) {
     throw new IllegalArgumentException("Invalid unique identifier");
   }
   final Pair<Currency, String> key = Pair.of(currency, name);
   final TreeMap<Instant, YieldCurveDefinition> value = _definitions.get(key);
   if (value == null) {
     throw new DataNotFoundException("UID '" + uid + "' not found");
   }
   if (_sourceVersionCorrection.getVersionAsOf() != null) {
     // Don't need to keep the old values before the one needed by "versionAsOfInstant"
     final Instant oldestNeeded = value.floorKey(_sourceVersionCorrection.getVersionAsOf());
     value.headMap(oldestNeeded).clear();
   } else {
     // Don't need any old values
     value.clear();
   }
   Instant now = Instant.now();
   value.put(now, document.getYieldCurveDefinition());
   document.setUniqueId(uid);
   changeManager().entityChanged(ChangeType.CHANGED, uid.getObjectId(), null, null, now);
   return document;
 }
 // -------------------------------------------------------------------------
 @Override
 public synchronized YieldCurveDefinitionDocument add(YieldCurveDefinitionDocument document) {
   ArgumentChecker.notNull(document, "document");
   ArgumentChecker.notNull(document.getYieldCurveDefinition(), "document.yieldCurveDefinition");
   final Currency currency = document.getYieldCurveDefinition().getCurrency();
   final String name = document.getYieldCurveDefinition().getName();
   final Pair<Currency, String> key = Pair.of(currency, name);
   if (_definitions.containsKey(key)) {
     throw new IllegalArgumentException("Duplicate definition");
   }
   final TreeMap<Instant, YieldCurveDefinition> value =
       new TreeMap<Instant, YieldCurveDefinition>();
   Instant now = Instant.now();
   value.put(now, document.getYieldCurveDefinition());
   _definitions.put(key, value);
   final UniqueId uid = UniqueId.of(getUniqueIdScheme(), name + "_" + currency.getCode());
   document.setUniqueId(uid);
   changeManager()
       .entityChanged(
           ChangeType.ADDED,
           document.getObjectId(),
           document.getVersionFromInstant(),
           document.getVersionToInstant(),
           now);
   return document;
 }
 @Override
 public CurveBuildingBlockBundle buildObject(
     final FudgeDeserializer deserializer, final FudgeMsg message) {
   final List<FudgeField> curveNames = message.getAllByName(CURVE_NAME_FIELD);
   final List<FudgeField> blocks = message.getAllByName(BLOCK_FIELD);
   final List<FudgeField> matrices = message.getAllByName(MATRIX_FIELD);
   final int n = curveNames.size();
   if (blocks.size() != n) {
     throw new IllegalStateException(
         "Should have one block for each curve name; have " + curveNames + " and " + blocks);
   }
   if (matrices.size() != n) {
     throw new IllegalStateException(
         "Should have one matrix for each curve name; have " + curveNames + " and " + matrices);
   }
   final LinkedHashMap<String, Pair<CurveBuildingBlock, DoubleMatrix2D>> data =
       new LinkedHashMap<>();
   for (int i = 0; i < n; i++) {
     final String curveName = (String) curveNames.get(i).getValue();
     final CurveBuildingBlock block =
         deserializer.fieldValueToObject(CurveBuildingBlock.class, blocks.get(i));
     final DoubleMatrix2D m =
         new DoubleMatrix2D(deserializer.fieldValueToObject(double[][].class, matrices.get(i)));
     data.put(curveName, Pair.of(block, m));
   }
   return new CurveBuildingBlockBundle(data);
 }
 @Override
 public CurveBuildingBlock buildObject(
     final FudgeDeserializer deserializer, final FudgeMsg message) {
   final List<FudgeField> curveNames = message.getAllByName(CURVE_NAME_FIELD);
   final List<FudgeField> startIndices = message.getAllByName(START_INDEX_FIELD);
   final int n = curveNames.size();
   if (startIndices.size() != n) {
     throw new IllegalStateException(
         "Should have one start index for each curve name; have "
             + curveNames
             + " and "
             + startIndices);
   }
   final List<FudgeField> numbers = message.getAllByName(NUMBER_FIELD);
   if (numbers.size() != n) {
     throw new IllegalStateException(
         "Should have one parameter number for each curve name; have "
             + curveNames
             + " and "
             + numbers);
   }
   final LinkedHashMap<String, Pair<Integer, Integer>> data = new LinkedHashMap<>();
   for (int i = 0; i < n; i++) {
     final String curveName = (String) curveNames.get(i).getValue();
     final Integer startIndex = ((Number) startIndices.get(i).getValue()).intValue();
     final Integer number = ((Number) numbers.get(i).getValue()).intValue();
     data.put(curveName, Pair.of(startIndex, number));
   }
   return new CurveBuildingBlock(data);
 }
 @SuppressWarnings("unchecked")
 public Pair<ResolveTask[], ResolvedValueProducer[]> getTasksProducing(
     final ValueSpecification valueSpecification) {
   do {
     final MapEx<ResolveTask, ResolvedValueProducer> tasks =
         getBuilder().getTasks(valueSpecification);
     if (tasks != null) {
       final ResolveTask[] resultTasks;
       final ResolvedValueProducer[] resultProducers;
       synchronized (tasks) {
         if (tasks.containsKey(null)) {
           continue;
         }
         if (tasks.isEmpty()) {
           return null;
         }
         resultTasks = new ResolveTask[tasks.size()];
         resultProducers = new ResolvedValueProducer[tasks.size()];
         int i = 0;
         for (final Map.Entry<ResolveTask, ResolvedValueProducer> task :
             (Set<Map.Entry<ResolveTask, ResolvedValueProducer>>) tasks.entrySet()) {
           // Don't ref-count the tasks; they're just used for parent comparisons
           resultTasks[i] = task.getKey();
           resultProducers[i++] = task.getValue();
           task.getValue().addRef(); // We're holding the task lock
         }
       }
       return Pair.of(resultTasks, resultProducers);
     } else {
       return null;
     }
   } while (true);
 }
 @Override
 public synchronized YieldCurveDefinitionDocument get(
     ObjectIdentifiable objectIdable, VersionCorrection versionCorrection) {
   ArgumentChecker.notNull(objectIdable, "objectIdable");
   ObjectId objectId = objectIdable.getObjectId();
   if (!getUniqueIdScheme().equals(objectId.getScheme())) {
     throw new DataNotFoundException(
         "Scheme '" + objectId.getScheme() + "' not valid for '" + getUniqueIdScheme() + "'");
   }
   final int i = objectId.getValue().indexOf('_');
   if (i <= 0) {
     throw new DataNotFoundException(
         "Identifier '" + objectId.getValue() + "' not valid for '" + getUniqueIdScheme() + "'");
   }
   final String name = objectId.getValue().substring(0, i);
   final String iso = objectId.getValue().substring(i + 1);
   final Currency currency;
   try {
     currency = Currency.of(iso);
   } catch (IllegalArgumentException e) {
     throw new DataNotFoundException(
         "Identifier '" + objectId.getValue() + "' not valid for '" + getUniqueIdScheme() + "'",
         e);
   }
   final TreeMap<Instant, YieldCurveDefinition> definitions =
       _definitions.get(Pair.of(currency, name));
   if (definitions == null) {
     throw new DataNotFoundException("Curve definition not found");
   }
   final YieldCurveDefinition definition = definitions.lastEntry().getValue();
   if (definition == null) {
     throw new DataNotFoundException("Curve definition not found");
   }
   return new YieldCurveDefinitionDocument(objectId.atLatestVersion(), definition);
 }
Example #13
0
  @SuppressWarnings("unchecked")
  @Test
  public void expandedRectangular() {
    Tenor[] xVals = new Tenor[] {Tenor.DAY, Tenor.ONE_WEEK, Tenor.TWO_WEEKS};
    Tenor[] yVals = new Tenor[] {Tenor.ONE_YEAR, Tenor.TWO_YEARS};
    double[] vols = new double[] {10, 11, 12, 13, 14, 15};
    Map<Pair<Tenor, Tenor>, Double> vol = Maps.newHashMap();
    for (int y = 0; y < yVals.length; y++) {
      for (int x = 0; x < xVals.length; x++) {
        vol.put(Pair.of(xVals[x], yVals[y]), vols[x + (y * xVals.length)]);
      }
    }
    String name = "test";
    UniqueIdentifiable target = Currency.USD;
    VolatilitySurfaceData<Tenor, Tenor> data =
        new VolatilitySurfaceData<>(name, name, target, xVals, yVals, vol);

    Map<String, Object> map =
        (Map<String, Object>)
            new VolatilitySurfaceDataFormatter()
                .format(data, null, TypeFormatter.Format.EXPANDED, null);
    assertEquals(Lists.newArrayList("1D", "7D", "14D"), map.get(SurfaceFormatterUtils.X_LABELS));
    assertEquals(Lists.newArrayList("1Y", "2Y"), map.get(SurfaceFormatterUtils.Y_LABELS));
    assertEquals(
        Lists.newArrayList(10d, 11d, 12d, 13d, 14d, 15d), map.get(SurfaceFormatterUtils.VOL));
  }
 public static Collection<Pair<ValueSpecification, Object>> getValues(
     final ViewComputationCache cache, final Collection<ValueSpecification> specifications) {
   final Collection<Pair<ValueSpecification, Object>> values =
       new ArrayList<Pair<ValueSpecification, Object>>(specifications.size());
   for (ValueSpecification specification : specifications) {
     values.add(Pair.of(specification, cache.getValue(specification)));
   }
   return values;
 }
 @Override
 public synchronized void remove(ObjectIdentifiable objectIdentifiable) {
   ArgumentChecker.notNull(objectIdentifiable, "objectIdentifiable");
   if (!getUniqueIdScheme().equals(objectIdentifiable.getObjectId().getScheme())) {
     throw new DataNotFoundException(
         "Scheme '"
             + objectIdentifiable.getObjectId().getScheme()
             + "' not valid for '"
             + getUniqueIdScheme()
             + "'");
   }
   final int i = objectIdentifiable.getObjectId().getValue().indexOf('_');
   if (i <= 0) {
     throw new DataNotFoundException(
         "Identifier '"
             + objectIdentifiable.getObjectId().getValue()
             + "' not valid for '"
             + getUniqueIdScheme()
             + "'");
   }
   final String name = objectIdentifiable.getObjectId().getValue().substring(0, i);
   final String iso = objectIdentifiable.getObjectId().getValue().substring(i + 1);
   final Currency currency;
   try {
     currency = Currency.of(iso);
   } catch (IllegalArgumentException e) {
     throw new DataNotFoundException(
         "Identifier '"
             + objectIdentifiable.getObjectId().getValue()
             + "' not valid for '"
             + getUniqueIdScheme()
             + "'",
         e);
   }
   final Pair<Currency, String> key = Pair.of(currency, name);
   if (_sourceVersionCorrection.getVersionAsOf() != null) {
     final TreeMap<Instant, YieldCurveDefinition> value = _definitions.get(key);
     if (value == null) {
       throw new DataNotFoundException("Curve definition not found");
     }
     // Don't need to keep the old values before the one needed by "versionAsOfInstant"
     final Instant oldestNeeded = value.floorKey(_sourceVersionCorrection.getVersionAsOf());
     if (oldestNeeded != null) {
       value.headMap(oldestNeeded).clear();
     }
     // Store a null to indicate the delete
     value.put(Instant.now(), null);
   } else {
     if (_definitions.remove(key) == null) {
       throw new DataNotFoundException("Curve definition not found");
     }
   }
   changeManager()
       .entityChanged(
           ChangeType.REMOVED, objectIdentifiable.getObjectId(), null, null, Instant.now());
 }
Example #16
0
 /**
  * @param priority The priority of the functions
  * @param interpolatorName The volatility surface interpolator name
  * @param leftExtrapolatorName The volatility surface left extrapolator name
  * @param rightExtrapolatorName The volatility surface right extrapolator name
  * @param propertyValuesByCurrencies Values for the properties per currency: an array of strings
  *     where the <i>i<sup>th</sup></i> currency has properties:
  *     <ul>
  *       <li><i>i</i> = first currency name,
  *       <li><i>i + 1</i> = first currency curve configuration name
  *       <li><i>i + 2</i> = first currency discounting curve name
  *       <li><i>i + 3</i> = second currency name,
  *       <li><i>i + 4</i> = second currency curve configuration name
  *       <li><i>i + 5</i> = second currency discounting curve name
  *       <li><i>i + 6</i> = surface name
  *     </ul>
  */
 public FXOptionBlackDefaults(
     final String priority,
     final String interpolatorName,
     final String leftExtrapolatorName,
     final String rightExtrapolatorName,
     final String... propertyValuesByCurrencies) {
   super(ComputationTargetType.SECURITY, true);
   ArgumentChecker.notNull(priority, "priority");
   ArgumentChecker.notNull(interpolatorName, "interpolator name");
   ArgumentChecker.notNull(leftExtrapolatorName, "left extrapolator name");
   ArgumentChecker.notNull(rightExtrapolatorName, "right extrapolator name");
   ArgumentChecker.notNull(propertyValuesByCurrencies, "property values by currency");
   ArgumentChecker.isTrue(
       propertyValuesByCurrencies.length % 7 == 0,
       "Must have two currencies, one curve config and discounting curve name per currency pair and one surface name");
   _priority = PriorityClass.valueOf(priority);
   _interpolatorName = interpolatorName;
   _leftExtrapolatorName = leftExtrapolatorName;
   _rightExtrapolatorName = rightExtrapolatorName;
   _propertyValuesByFirstCurrency = new HashMap<String, Pair<String, String>>();
   _propertyValuesBySecondCurrency = new HashMap<String, Pair<String, String>>();
   _surfaceNameByCurrencyPair = new HashMap<Pair<String, String>, String>();
   for (int i = 0; i < propertyValuesByCurrencies.length; i += 7) {
     final String firstCurrency = propertyValuesByCurrencies[i];
     final Pair<String, String> firstCurrencyValues =
         Pair.of(propertyValuesByCurrencies[i + 1], propertyValuesByCurrencies[i + 2]);
     final String secondCurrency = propertyValuesByCurrencies[i + 3];
     ArgumentChecker.isFalse(
         firstCurrency.equals(secondCurrency),
         "The two currencies must not be equal; have {} and {}",
         firstCurrency,
         secondCurrency);
     final Pair<String, String> secondCurrencyValues =
         Pair.of(propertyValuesByCurrencies[i + 4], propertyValuesByCurrencies[i + 5]);
     final String surfaceName = propertyValuesByCurrencies[i + 6];
     _propertyValuesByFirstCurrency.put(firstCurrency, firstCurrencyValues);
     _propertyValuesBySecondCurrency.put(secondCurrency, secondCurrencyValues);
     _surfaceNameByCurrencyPair.put(Pair.of(firstCurrency, secondCurrency), surfaceName);
   }
 }
  @Override
  public List<UniqueId> replaceAllVersions(
      ObjectIdentifiable objectIdentifiable,
      List<YieldCurveDefinitionDocument> replacementDocuments) {
    ArgumentChecker.notNull(replacementDocuments, "replacementDocuments");
    ArgumentChecker.notNull(objectIdentifiable, "objectIdentifiable");

    final Instant now = Instant.now();

    for (YieldCurveDefinitionDocument replacementDocument : replacementDocuments) {
      ArgumentChecker.notNull(replacementDocument, "document");
      ArgumentChecker.notNull(
          replacementDocument.getYieldCurveDefinition(), "document.yieldCurveDefinition");
      final Currency currency = replacementDocument.getYieldCurveDefinition().getCurrency();
      final String name = replacementDocument.getYieldCurveDefinition().getName();
      final UniqueId id = UniqueId.of(getUniqueIdScheme(), name + "_" + currency.getCode());
      ArgumentChecker.isTrue(id.equals(objectIdentifiable), "Invalid object identifier");
    }

    YieldCurveDefinitionDocument storedDocument = get(objectIdentifiable, null);
    if (storedDocument == null) {
      throw new DataNotFoundException("Document not found: " + objectIdentifiable);
    }
    final Currency currency = storedDocument.getYieldCurveDefinition().getCurrency();
    final String name = storedDocument.getYieldCurveDefinition().getName();
    Pair<Currency, String> key = Pair.of(currency, name);

    final TreeMap<Instant, YieldCurveDefinition> value = _definitions.get(key);
    if (value == null) {
      throw new DataNotFoundException("OID '" + objectIdentifiable + "' not found");
    }
    value.clear();

    List<YieldCurveDefinitionDocument> orderedReplacementDocuments =
        MasterUtils.adjustVersionInstants(now, null, null, replacementDocuments);

    final Instant lowestVersionFrom = orderedReplacementDocuments.get(0).getVersionFromInstant();

    ArgumentChecker.notNull(
        lowestVersionFrom, "You must define version from of the first document");

    for (YieldCurveDefinitionDocument replacementDocument : orderedReplacementDocuments) {
      value.put(
          replacementDocument.getVersionFromInstant(),
          replacementDocument.getYieldCurveDefinition());
      changeManager()
          .entityChanged(ChangeType.CHANGED, replacementDocument.getObjectId(), null, null, now);
    }
    return MasterUtils.mapToUniqueIDs(orderedReplacementDocuments);
  }
  @Override
  public ObjectsPair<Collection<LiveDataSpecification>, Collection<LiveDataSpecification>>
      splitShouldFake(
          FakeSubscriptionBloombergLiveDataServer server, Collection<LiveDataSpecification> specs) {
    if (specs.isEmpty()) {
      return Pair.of(
          (Collection<LiveDataSpecification>) new ArrayList<LiveDataSpecification>(),
          (Collection<LiveDataSpecification>) new ArrayList<LiveDataSpecification>());
    }

    Set<LiveDataSpecification> fakes = new HashSet<LiveDataSpecification>();
    Set<LiveDataSpecification> underlyings = new HashSet<LiveDataSpecification>();

    for (LiveDataSpecification liveDataSpecification : specs) {
      if (shouldFake(liveDataSpecification)) {
        fakes.add(liveDataSpecification);
      } else {
        underlyings.add(liveDataSpecification);
      }
    }

    return Pair.of(
        (Collection<LiveDataSpecification>) underlyings, (Collection<LiveDataSpecification>) fakes);
  }
 @Override
 public ComputedValue getComputedValue(final ValueRequirement requirement) {
   final Pair<String, Object> key =
       Pair.of(
           requirement.getValueName(),
           targetSpecKey(_resolver.getTargetSpecification(requirement.getTargetReference())));
   final ComputedValue[] values = _valuesByRequirement.get(key);
   if (values != null) {
     for (final ComputedValue value : values) {
       // Shortcut to check the properties as we already know the name and target match
       if (requirement.getConstraints().isSatisfiedBy(value.getSpecification().getProperties())) {
         return value;
       }
     }
   }
   return null;
 }
 @Override
 public YieldCurveDefinition getDefinition(
     final Currency currency, final String name, final VersionCorrection versionCorrection) {
   ArgumentChecker.notNull(currency, "currency");
   ArgumentChecker.notNull(name, "name");
   final TreeMap<Instant, YieldCurveDefinition> definitions =
       _definitions.get(Pair.of(currency, name));
   if (definitions == null) {
     return null;
   }
   final Map.Entry<Instant, YieldCurveDefinition> entry =
       definitions.floorEntry(versionCorrection.getVersionAsOf());
   if (entry == null) {
     return null;
   }
   return entry.getValue();
 }
Example #21
0
  @Override
  public ViewCalculationResultModel buildObject(FudgeDeserializer deserializer, FudgeMsg message) {
    final Map<ComputationTargetSpecification, Map<Pair<String, ValueProperties>, ComputedValue>>
        mapNames =
            new HashMap<
                ComputationTargetSpecification,
                Map<Pair<String, ValueProperties>, ComputedValue>>();
    for (FudgeField field : message) {
      final ComputedValue value = deserializer.fieldValueToObject(ComputedValue.class, field);
      final ComputationTargetSpecification target =
          value.getSpecification().getTargetSpecification();
      if (!mapNames.containsKey(target)) {
        mapNames.put(target, new HashMap<Pair<String, ValueProperties>, ComputedValue>());
      }
      mapNames
          .get(target)
          .put(
              Pair.of(
                  value.getSpecification().getValueName(),
                  value.getSpecification().getProperties()),
              value);
    }
    return new ViewCalculationResultModel() {

      @Override
      public Collection<ComputationTargetSpecification> getAllTargets() {
        return mapNames.keySet();
      }

      @Override
      public Map<Pair<String, ValueProperties>, ComputedValue> getValues(
          ComputationTargetSpecification target) {
        return mapNames.get(target);
      }

      @Override
      public Collection<ComputedValue> getAllValues(ComputationTargetSpecification target) {
        Map<Pair<String, ValueProperties>, ComputedValue> targetValuesMap = mapNames.get(target);
        return targetValuesMap != null
            ? Collections.unmodifiableCollection(targetValuesMap.values())
            : null;
      }
    };
  }
 // -------------------------------------------------------------------------
 @Override
 public synchronized YieldCurveDefinition getDefinition(Currency currency, String name) {
   ArgumentChecker.notNull(currency, "currency");
   ArgumentChecker.notNull(name, "name");
   final TreeMap<Instant, YieldCurveDefinition> definitions =
       _definitions.get(Pair.of(currency, name));
   if (definitions == null) {
     return null;
   }
   final Map.Entry<Instant, YieldCurveDefinition> entry;
   if (_sourceVersionCorrection.getVersionAsOf() == null) {
     entry = definitions.lastEntry();
   } else {
     entry = definitions.floorEntry(_sourceVersionCorrection.getVersionAsOf());
   }
   if (entry == null) {
     return null;
   }
   return entry.getValue();
 }
 @Override
 public CompiledFunctionRepository compile(
     final FunctionRepository repository,
     final FunctionCompilationContext context,
     final ExecutorService executor,
     final InstantProvider atInstantProvider) {
   clearInvalidCache(context.getFunctionInitId());
   final Instant atInstant = Instant.of(atInstantProvider);
   final Pair<FunctionRepository, Instant> key = Pair.of(repository, atInstant);
   // Try a previous compilation
   final InMemoryCompiledFunctionRepository previous = getPreviousCompilation(key);
   if (previous != null) {
     if (previous.getLatestInvocationTime() == null) {
       return previous;
     } else {
       if (!atInstant.isAfter(previous.getLatestInvocationTime())) {
         return previous;
       }
     }
   }
   // Try a future compilation
   final InMemoryCompiledFunctionRepository next = getNextCompilation(key);
   if (next != null) {
     if (next.getEarliestInvocationTime() == null) {
       return next;
     } else {
       if (!atInstant.isBefore(next.getEarliestInvocationTime())) {
         return next;
       }
     }
   }
   // Try the exact timestamp
   InMemoryCompiledFunctionRepository compiled = getCachedCompilation(key);
   if (compiled != null) {
     return compiled;
   }
   // Create a compilation, salvaging results from previous and next if possible
   compiled = compile(context, repository, atInstant, previous, next, executor);
   cacheCompilation(key, compiled);
   return compiled;
 }
 private void addValue(final ComputedValue value) {
   ArgumentChecker.notNull(value, "Computed Value");
   if (value.getValue() instanceof ComputedValue) {
     throw new IllegalArgumentException("Double-nested value");
   }
   _values.add(value);
   _valuesByRequirementName.put(value.getSpecification().getValueName(), value);
   final Pair<String, Object> key =
       Pair.of(
           value.getSpecification().getValueName(),
           targetSpecKey(value.getSpecification().getTargetSpecification()));
   final ComputedValue[] prev = _valuesByRequirement.get(key);
   if (prev == null) {
     _valuesByRequirement.put(key, new ComputedValue[] {value});
   } else {
     final ComputedValue[] values = new ComputedValue[prev.length + 1];
     System.arraycopy(prev, 0, values, 0, prev.length);
     values[prev.length] = value;
     _valuesByRequirement.put(key, values);
   }
 }
 protected FXOptionSecurity createFXOptionSecurity(final Bundle bundle) {
   final Currency putCurrency = bundle._firstCurrency;
   final Currency callCurrency = bundle._secondCurrency;
   final double putAmount = bundle._firstCurrency.equals(Currency.JPY) ? NOTIONAL * 100 : NOTIONAL;
   final ZonedDateTime settlementDate = bundle._tradeDate.plusDays(bundle._daysOffset);
   final Double fxRate =
       getApproxFXRate(
           settlementDate.toLocalDate(), Pair.of(bundle._firstCurrency, bundle._secondCurrency));
   if (fxRate == null) {
     return null;
   }
   final double callAmount = NOTIONAL * fxRate;
   final Expiry expiry = new Expiry(settlementDate, ExpiryAccuracy.DAY_MONTH_YEAR);
   final String dateString = settlementDate.toString(DATE_FORMATTER);
   final FXOptionSecurity fxOptionSecurity =
       new FXOptionSecurity(
           putCurrency,
           callCurrency,
           putAmount,
           callAmount,
           expiry,
           settlementDate,
           bundle._long,
           new EuropeanExerciseType());
   final String callAmountString = NOTIONAL_FORMATTER.format(callAmount);
   final String putAmountString = NOTIONAL_FORMATTER.format(putAmount);
   fxOptionSecurity.setName(
       (bundle._long ? "Long " : "Short ")
           + "put "
           + putCurrency
           + " "
           + putAmountString
           + ", call "
           + callCurrency
           + " "
           + callAmountString
           + " on "
           + dateString);
   return fxOptionSecurity;
 }
 @Override
 public synchronized YieldCurveDefinitionDocument addOrUpdate(
     YieldCurveDefinitionDocument document) {
   ArgumentChecker.notNull(document, "document");
   ArgumentChecker.notNull(document.getYieldCurveDefinition(), "document.yieldCurveDefinition");
   final Currency currency = document.getYieldCurveDefinition().getCurrency();
   final String name = document.getYieldCurveDefinition().getName();
   final Pair<Currency, String> key = Pair.of(currency, name);
   TreeMap<Instant, YieldCurveDefinition> value = _definitions.get(key);
   final UniqueId uid = UniqueId.of(getUniqueIdScheme(), name + "_" + currency.getCode());
   Instant now = Instant.now();
   if (value != null) {
     if (_sourceVersionCorrection.getVersionAsOf() != null) {
       // Don't need to keep the old values before the one needed by "versionAsOfInstant"
       final Instant oldestNeeded = value.floorKey(_sourceVersionCorrection.getVersionAsOf());
       if (oldestNeeded != null) {
         value.headMap(oldestNeeded).clear();
       }
     } else {
       // Don't need any old values
       value.clear();
     }
     value.put(now, document.getYieldCurveDefinition());
     changeManager().entityChanged(ChangeType.CHANGED, uid.getObjectId(), null, null, now);
   } else {
     value = new TreeMap<Instant, YieldCurveDefinition>();
     value.put(now, document.getYieldCurveDefinition());
     _definitions.put(key, value);
     changeManager()
         .entityChanged(
             ChangeType.ADDED,
             uid.getObjectId(),
             document.getVersionFromInstant(),
             document.getVersionToInstant(),
             now);
   }
   document.setUniqueId(uid);
   return document;
 }
 @Override
 public synchronized YieldCurveDefinitionDocument get(UniqueId uid) {
   ArgumentChecker.notNull(uid, "objectIdentifiable");
   if (!uid.isLatest()) {
     throw new IllegalArgumentException(
         "Only latest version supported by '" + getUniqueIdScheme() + "'");
   }
   if (!getUniqueIdScheme().equals(uid.getScheme())) {
     throw new DataNotFoundException(
         "Scheme '" + uid.getScheme() + "' not valid for '" + getUniqueIdScheme() + "'");
   }
   final int i = uid.getValue().indexOf('_');
   if (i <= 0) {
     throw new DataNotFoundException(
         "Identifier '" + uid.getValue() + "' not valid for '" + getUniqueIdScheme() + "'");
   }
   final String name = uid.getValue().substring(0, i);
   final String iso = uid.getValue().substring(i + 1);
   final Currency currency;
   try {
     currency = Currency.of(iso);
   } catch (IllegalArgumentException e) {
     throw new DataNotFoundException(
         "Identifier '" + uid.getValue() + "' not valid for '" + getUniqueIdScheme() + "'", e);
   }
   final TreeMap<Instant, YieldCurveDefinition> definitions =
       _definitions.get(Pair.of(currency, name));
   if (definitions == null) {
     throw new DataNotFoundException("Curve definition not found");
   }
   final YieldCurveDefinition definition = definitions.lastEntry().getValue();
   if (definition == null) {
     throw new DataNotFoundException("Curve definition not found");
   }
   return new YieldCurveDefinitionDocument(uid, definition);
 }
Example #28
0
 @Override
 protected Set<String> getDefaultValue(
     final FunctionCompilationContext context,
     final ComputationTarget target,
     final ValueRequirement desiredValue,
     final String propertyName) {
   final FinancialSecurity security = (FinancialSecurity) target.getSecurity();
   final String putCurrency = security.accept(ForexVisitors.getPutCurrencyVisitor()).getCode();
   final String callCurrency = security.accept(ForexVisitors.getCallCurrencyVisitor()).getCode();
   if (!(_propertyValuesByFirstCurrency.containsKey(putCurrency)
       || _propertyValuesBySecondCurrency.containsKey(putCurrency))) {
     s_logger.error(
         "Could not get config for put currency " + putCurrency + "; should never happen");
     return null;
   }
   if (!(_propertyValuesByFirstCurrency.containsKey(callCurrency)
       || _propertyValuesBySecondCurrency.containsKey(callCurrency))) {
     s_logger.error(
         "Could not get config for call currency " + callCurrency + "; should never happen");
     return null;
   }
   final String putCurveConfig, callCurveConfig, putCurve, callCurve;
   if (_propertyValuesByFirstCurrency.containsKey(putCurrency)) {
     final Pair<String, String> firstCurrencyValues =
         _propertyValuesByFirstCurrency.get(putCurrency);
     putCurveConfig = firstCurrencyValues.getFirst();
     putCurve = firstCurrencyValues.getSecond();
     final Pair<String, String> secondCurrencyValues =
         _propertyValuesBySecondCurrency.get(callCurrency);
     callCurveConfig = secondCurrencyValues.getFirst();
     callCurve = secondCurrencyValues.getSecond();
   } else {
     final Pair<String, String> firstCurrencyValues =
         _propertyValuesByFirstCurrency.get(callCurrency);
     callCurveConfig = firstCurrencyValues.getFirst();
     callCurve = firstCurrencyValues.getSecond();
     final Pair<String, String> secondCurrencyValues =
         _propertyValuesBySecondCurrency.get(putCurrency);
     putCurveConfig = secondCurrencyValues.getFirst();
     putCurve = secondCurrencyValues.getSecond();
   }
   if (FXOptionBlackFunction.PUT_CURVE_CALC_CONFIG.equals(propertyName)) {
     return Collections.singleton(putCurveConfig);
   }
   if (FXOptionBlackFunction.PUT_CURVE.equals(propertyName)) {
     return Collections.singleton(putCurve);
   }
   if (FXOptionBlackFunction.CALL_CURVE_CALC_CONFIG.equals(propertyName)) {
     return Collections.singleton(callCurveConfig);
   }
   if (FXOptionBlackFunction.CALL_CURVE.equals(propertyName)) {
     return Collections.singleton(callCurve);
   }
   if (InterpolatedDataProperties.X_INTERPOLATOR_NAME.equals(propertyName)) {
     return Collections.singleton(_interpolatorName);
   }
   if (InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME.equals(propertyName)) {
     return Collections.singleton(_leftExtrapolatorName);
   }
   if (InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME.equals(propertyName)) {
     return Collections.singleton(_rightExtrapolatorName);
   }
   if (ValuePropertyNames.SURFACE.equals(propertyName)) {
     Pair<String, String> pair = Pair.of(putCurrency, callCurrency);
     if (_surfaceNameByCurrencyPair.containsKey(pair)) {
       return Collections.singleton(_surfaceNameByCurrencyPair.get(pair));
     }
     pair = Pair.of(callCurrency, putCurrency);
     if (_surfaceNameByCurrencyPair.containsKey(pair)) {
       return Collections.singleton(_surfaceNameByCurrencyPair.get(pair));
     }
   }
   return null;
 }
 @Override
 public Set<ComputedValue> execute(
     final FunctionExecutionContext executionContext,
     final FunctionInputs inputs,
     final ComputationTarget target,
     final Set<ValueRequirement> desiredValues) {
   final Clock snapshotClock = executionContext.getValuationClock();
   final ZonedDateTime now = snapshotClock.zonedDateTime();
   final FinancialSecurity security = (FinancialSecurity) target.getSecurity();
   final InstrumentDefinition<InstrumentDerivative> definition =
       (InstrumentDefinition<InstrumentDerivative>) security.accept(VISITOR);
   final Currency putCurrency = security.accept(ForexVisitors.getPutCurrencyVisitor());
   final Currency callCurrency = security.accept(ForexVisitors.getCallCurrencyVisitor());
   final ValueRequirement desiredValue = desiredValues.iterator().next();
   final String putCurveName = desiredValue.getConstraint(PROPERTY_PUT_CURVE);
   final String callCurveName = desiredValue.getConstraint(PROPERTY_CALL_CURVE);
   final String surfaceName = desiredValue.getConstraint(ValuePropertyNames.SURFACE);
   final String putForwardCurveName = desiredValue.getConstraint(PROPERTY_PUT_FORWARD_CURVE);
   final String callForwardCurveName = desiredValue.getConstraint(PROPERTY_CALL_FORWARD_CURVE);
   final String putCurveCalculationMethod =
       desiredValue.getConstraint(PROPERTY_PUT_CURVE_CALCULATION_METHOD);
   final String callCurveCalculationMethod =
       desiredValue.getConstraint(PROPERTY_CALL_CURVE_CALCULATION_METHOD);
   final String interpolatorName =
       desiredValue.getConstraint(InterpolatedDataProperties.X_INTERPOLATOR_NAME);
   final String leftExtrapolatorName =
       desiredValue.getConstraint(InterpolatedDataProperties.LEFT_X_EXTRAPOLATOR_NAME);
   final String rightExtrapolatorName =
       desiredValue.getConstraint(InterpolatedDataProperties.RIGHT_X_EXTRAPOLATOR_NAME);
   final String spread = desiredValue.getConstraint(PROPERTY_CALL_SPREAD_VALUE);
   final double spreadValue = Double.parseDouble(spread);
   final String fullPutCurveName = putCurveName + "_" + putCurrency.getCode();
   final String fullCallCurveName = callCurveName + "_" + callCurrency.getCode();
   final String[] curveNames;
   if (FXUtils.isInBaseQuoteOrder(
       putCurrency, callCurrency)) { // To get Base/quote in market standard order.
     curveNames = new String[] {fullPutCurveName, fullCallCurveName};
   } else {
     curveNames = new String[] {fullCallCurveName, fullPutCurveName};
   }
   final YieldAndDiscountCurve putFundingCurve = getCurve(inputs, putCurrency, putCurveName);
   final YieldAndDiscountCurve callFundingCurve = getCurve(inputs, callCurrency, callCurveName);
   final YieldAndDiscountCurve[] curves;
   final Map<String, Currency> curveCurrency = new HashMap<String, Currency>();
   curveCurrency.put(fullPutCurveName, putCurrency);
   curveCurrency.put(fullCallCurveName, callCurrency);
   final String[] allCurveNames;
   final Currency ccy1;
   final Currency ccy2;
   if (FXUtils.isInBaseQuoteOrder(
       putCurrency, callCurrency)) { // To get Base/quote in market standard order.
     ccy1 = putCurrency;
     ccy2 = callCurrency;
     curves = new YieldAndDiscountCurve[] {putFundingCurve, callFundingCurve};
     allCurveNames = new String[] {fullPutCurveName, fullCallCurveName};
   } else {
     curves = new YieldAndDiscountCurve[] {callFundingCurve, putFundingCurve};
     allCurveNames = new String[] {fullCallCurveName, fullPutCurveName};
     ccy1 = callCurrency;
     ccy2 = putCurrency;
   }
   final InstrumentDerivative fxOption = definition.toDerivative(now, curveNames);
   final YieldCurveBundle yieldCurves = new YieldCurveBundle(allCurveNames, curves);
   final Object spotObject = inputs.getValue(ValueRequirementNames.SPOT_RATE);
   if (spotObject == null) {
     throw new OpenGammaRuntimeException("Could not get spot rate");
   }
   final double spot = (Double) spotObject;
   final ValueRequirement fxVolatilitySurfaceRequirement =
       getSurfaceRequirement(
           surfaceName,
           putCurrency,
           callCurrency,
           interpolatorName,
           leftExtrapolatorName,
           rightExtrapolatorName);
   final Object volatilitySurfaceObject = inputs.getValue(fxVolatilitySurfaceRequirement);
   if (volatilitySurfaceObject == null) {
     throw new OpenGammaRuntimeException("Could not get " + fxVolatilitySurfaceRequirement);
   }
   final SmileDeltaTermStructureParametersStrikeInterpolation smiles =
       (SmileDeltaTermStructureParametersStrikeInterpolation) volatilitySurfaceObject;
   final FXMatrix fxMatrix = new FXMatrix(ccy1, ccy2, spot);
   final ValueProperties.Builder properties =
       getResultProperties(
           putCurveName,
           putForwardCurveName,
           putCurveCalculationMethod,
           callCurveName,
           callForwardCurveName,
           callCurveCalculationMethod,
           surfaceName,
           spread,
           interpolatorName,
           leftExtrapolatorName,
           rightExtrapolatorName,
           target);
   final ValueSpecification spec =
       new ValueSpecification(_valueRequirementName, target.toSpecification(), properties.get());
   final YieldCurveBundle curvesWithFX =
       new YieldCurveBundle(fxMatrix, curveCurrency, yieldCurves.getCurvesMap());
   final SmileDeltaTermStructureDataBundle smileBundle =
       new SmileDeltaTermStructureDataBundle(curvesWithFX, smiles, Pair.of(ccy1, ccy2));
   return getResult(fxOption, spreadValue, smileBundle, spec);
 }
  private void addNodes(
      final ManageablePortfolioNode rootNode,
      final String underlying,
      final boolean includeUnderlying,
      final Period[] expiries) {
    final ExternalId ticker = ExternalSchemes.bloombergTickerSecurityId(underlying);
    ManageableSecurity underlyingSecurity = null;
    if (includeUnderlying) {
      underlyingSecurity = getOrLoadEquity(ticker);
    }

    final ExternalIdBundle bundle =
        underlyingSecurity == null
            ? ExternalIdBundle.of(ticker)
            : underlyingSecurity.getExternalIdBundle();
    final HistoricalTimeSeriesInfoDocument timeSeriesInfo = getOrLoadTimeSeries(ticker, bundle);
    final double estimatedCurrentStrike = getOrLoadMostRecentPoint(timeSeriesInfo);
    final Set<ExternalId> optionChain = getOptionChain(ticker);

    // TODO: reuse positions/nodes?
    final String longName = underlyingSecurity == null ? "" : underlyingSecurity.getName();
    final String formattedName =
        MessageFormatter.format("[{}] {}", underlying, longName).getMessage();
    final ManageablePortfolioNode equityNode = new ManageablePortfolioNode(formattedName);

    final BigDecimal underlyingAmount =
        VALUE_OF_UNDERLYING.divide(
            BigDecimal.valueOf(estimatedCurrentStrike), BigDecimal.ROUND_HALF_EVEN);

    if (includeUnderlying) {
      addPosition(equityNode, underlyingAmount, ticker);
    }

    final TreeMap<LocalDate, Set<BloombergTickerParserEQOption>> optionsByExpiry =
        new TreeMap<LocalDate, Set<BloombergTickerParserEQOption>>();
    for (final ExternalId optionTicker : optionChain) {
      s_logger.debug("Got option {}", optionTicker);

      final BloombergTickerParserEQOption optionInfo =
          BloombergTickerParserEQOption.getOptionParser(optionTicker);
      s_logger.debug("Got option info {}", optionInfo);

      final LocalDate key = optionInfo.getExpiry();
      Set<BloombergTickerParserEQOption> set = optionsByExpiry.get(key);
      if (set == null) {
        set = new HashSet<BloombergTickerParserEQOption>();
        optionsByExpiry.put(key, set);
      }
      set.add(optionInfo);
    }
    final Set<ExternalId> tickersToLoad = new HashSet<ExternalId>();

    final BigDecimal expiryCount = BigDecimal.valueOf(expiries.length);
    final BigDecimal defaultAmountAtExpiry =
        underlyingAmount.divide(expiryCount, BigDecimal.ROUND_DOWN);
    final BigDecimal spareAmountAtExpiry = defaultAmountAtExpiry.add(BigDecimal.ONE);
    int spareCount =
        underlyingAmount.subtract(defaultAmountAtExpiry.multiply(expiryCount)).intValue();

    for (final Period bucketPeriod : expiries) {
      final ManageablePortfolioNode bucketNode =
          new ManageablePortfolioNode(bucketPeriod.toString().substring(1));

      final LocalDate nowish =
          LocalDate.now()
              .withDayOfMonth(
                  20); // This avoids us picking different options every time this script is run
      final LocalDate targetExpiry = nowish.plus(bucketPeriod);
      final LocalDate chosenExpiry = optionsByExpiry.floorKey(targetExpiry);
      if (chosenExpiry == null) {
        s_logger.info("No options for {} on {}", targetExpiry, underlying);
        continue;
      }
      s_logger.info(
          "Using time {} for bucket {} ({})",
          new Object[] {chosenExpiry, bucketPeriod, targetExpiry});

      final Set<BloombergTickerParserEQOption> optionsAtExpiry = optionsByExpiry.get(chosenExpiry);
      final TreeMap<Double, Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>>
          optionsByStrike = new TreeMap<>();
      for (final BloombergTickerParserEQOption option : optionsAtExpiry) {
        //        s_logger.info("option {}", option);
        final double key = option.getStrike();
        Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption> pair =
            optionsByStrike.get(key);
        if (pair == null) {
          pair = Pair.of(null, null);
        }
        if (option.getOptionType() == OptionType.CALL) {
          pair = Pair.of(option, pair.getSecond());
        } else {
          pair = Pair.of(pair.getFirst(), option);
        }
        optionsByStrike.put(key, pair);
      }

      // cascading collar?
      final BigDecimal amountAtExpiry =
          spareCount-- > 0 ? spareAmountAtExpiry : defaultAmountAtExpiry;

      s_logger.info(" est strike {}", estimatedCurrentStrike);
      final Double[] strikes = optionsByStrike.keySet().toArray(new Double[0]);

      int strikeIndex = Arrays.binarySearch(strikes, estimatedCurrentStrike);
      if (strikeIndex < 0) {
        strikeIndex = -(1 + strikeIndex);
      }
      s_logger.info(
          "strikes length {} index {} strike of index {}",
          new Object[] {
            Integer.valueOf(strikes.length),
            Integer.valueOf(strikeIndex),
            Double.valueOf(strikes[strikeIndex])
          });

      int minIndex = strikeIndex - _numOptions;
      minIndex = Math.max(0, minIndex);
      int maxIndex = strikeIndex + _numOptions;
      maxIndex = Math.min(strikes.length - 1, maxIndex);

      s_logger.info("min {} max {}", Integer.valueOf(minIndex), Integer.valueOf(maxIndex));
      final StringBuffer sb = new StringBuffer("strikes: [");
      for (int j = minIndex; j <= maxIndex; j++) {
        sb.append(" ");
        sb.append(strikes[j]);
      }
      sb.append(" ]");
      s_logger.info(sb.toString());

      // Short Calls
      final ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>> calls =
          new ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>>();
      for (int j = minIndex; j < strikeIndex; j++) {
        final Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption> pair =
            optionsByStrike.get(strikes[j]);
        if (pair == null) {
          throw new OpenGammaRuntimeException("no pair for strike" + strikes[j]);
        }
        calls.add(pair);
      }
      spreadOptions(
          bucketNode,
          calls,
          OptionType.CALL,
          -1,
          tickersToLoad,
          amountAtExpiry,
          includeUnderlying,
          calls.size());

      // Long Puts
      final ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>> puts =
          new ArrayList<Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption>>();
      for (int j = strikeIndex + 1; j <= maxIndex; j++) {
        final Pair<BloombergTickerParserEQOption, BloombergTickerParserEQOption> pair =
            optionsByStrike.get(strikes[j]);
        if (pair == null) {
          throw new OpenGammaRuntimeException("no pair for strike" + strikes[j]);
        }
        puts.add(pair);
      }
      spreadOptions(
          bucketNode,
          puts,
          OptionType.PUT,
          1,
          tickersToLoad,
          amountAtExpiry,
          includeUnderlying,
          puts.size());

      if (bucketNode.getChildNodes().size() + bucketNode.getPositionIds().size() > 0) {
        equityNode.addChildNode(bucketNode); // Avoid generating empty nodes
      }
    }

    for (final ExternalId optionTicker : tickersToLoad) {
      final ManageableSecurity loaded = getOrLoadSecurity(optionTicker);
      if (loaded == null) {
        throw new OpenGammaRuntimeException("Unexpected option type " + loaded);
      }

      // TODO [LAPANA-29] Should be able to do this for index options too
      if (includeUnderlying) {
        try {
          final HistoricalTimeSeriesInfoDocument loadedTs =
              getOrLoadTimeSeries(optionTicker, loaded.getExternalIdBundle());
          if (loadedTs == null) {
            throw new OpenGammaRuntimeException("Failed to get time series for " + loaded);
          }
        } catch (final Exception ex) {
          s_logger.info("Failed to get time series for " + loaded, ex);
        }
      }
    }

    if (equityNode.getPositionIds().size() + equityNode.getChildNodes().size() > 0) {
      rootNode.addChildNode(equityNode);
    }
  }