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);