public final class DebugView extends FrameLayout { private static final DateTimeFormatter DATE_DISPLAY_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm a", Locale.US).withZone(ZoneId.systemDefault()); @Bind(R.id.debug_contextual_title) View contextualTitleView; @Bind(R.id.debug_contextual_list) LinearLayout contextualListView; @Bind(R.id.debug_network_endpoint) Spinner endpointView; @Bind(R.id.debug_network_endpoint_edit) View endpointEditView; @Bind(R.id.debug_network_delay) Spinner networkDelayView; @Bind(R.id.debug_network_variance) Spinner networkVarianceView; @Bind(R.id.debug_network_error) Spinner networkErrorView; @Bind(R.id.debug_network_proxy) Spinner networkProxyView; @Bind(R.id.debug_network_logging) Spinner networkLoggingView; @Bind(R.id.debug_capture_intents) Switch captureIntentsView; @Bind(R.id.debug_repositories_response) Spinner repositoriesResponseView; @Bind(R.id.debug_ui_animation_speed) Spinner uiAnimationSpeedView; @Bind(R.id.debug_ui_pixel_grid) Switch uiPixelGridView; @Bind(R.id.debug_ui_pixel_ratio) Switch uiPixelRatioView; @Bind(R.id.debug_ui_scalpel) Switch uiScalpelView; @Bind(R.id.debug_ui_scalpel_wireframe) Switch uiScalpelWireframeView; @Bind(R.id.debug_build_name) TextView buildNameView; @Bind(R.id.debug_build_code) TextView buildCodeView; @Bind(R.id.debug_build_sha) TextView buildShaView; @Bind(R.id.debug_build_date) TextView buildDateView; @Bind(R.id.debug_device_make) TextView deviceMakeView; @Bind(R.id.debug_device_model) TextView deviceModelView; @Bind(R.id.debug_device_resolution) TextView deviceResolutionView; @Bind(R.id.debug_device_density) TextView deviceDensityView; @Bind(R.id.debug_device_release) TextView deviceReleaseView; @Bind(R.id.debug_device_api) TextView deviceApiView; @Bind(R.id.debug_picasso_indicators) Switch picassoIndicatorView; @Bind(R.id.debug_picasso_cache_size) TextView picassoCacheSizeView; @Bind(R.id.debug_picasso_cache_hit) TextView picassoCacheHitView; @Bind(R.id.debug_picasso_cache_miss) TextView picassoCacheMissView; @Bind(R.id.debug_picasso_decoded) TextView picassoDecodedView; @Bind(R.id.debug_picasso_decoded_total) TextView picassoDecodedTotalView; @Bind(R.id.debug_picasso_decoded_avg) TextView picassoDecodedAvgView; @Bind(R.id.debug_picasso_transformed) TextView picassoTransformedView; @Bind(R.id.debug_picasso_transformed_total) TextView picassoTransformedTotalView; @Bind(R.id.debug_picasso_transformed_avg) TextView picassoTransformedAvgView; @Bind(R.id.debug_okhttp_cache_max_size) TextView okHttpCacheMaxSizeView; @Bind(R.id.debug_okhttp_cache_write_error) TextView okHttpCacheWriteErrorView; @Bind(R.id.debug_okhttp_cache_request_count) TextView okHttpCacheRequestCountView; @Bind(R.id.debug_okhttp_cache_network_count) TextView okHttpCacheNetworkCountView; @Bind(R.id.debug_okhttp_cache_hit_count) TextView okHttpCacheHitCountView; @Inject OkHttpClient client; @Inject @Named("Api") OkHttpClient apiClient; @Inject Picasso picasso; @Inject LumberYard lumberYard; @Inject @IsMockMode boolean isMockMode; @Inject @ApiEndpoint Preference<String> networkEndpoint; @Inject Preference<InetSocketAddress> networkProxyAddress; @Inject @CaptureIntents Preference<Boolean> captureIntents; @Inject @AnimationSpeed Preference<Integer> animationSpeed; @Inject @PicassoDebugging Preference<Boolean> picassoDebugging; @Inject @PixelGridEnabled Preference<Boolean> pixelGridEnabled; @Inject @PixelRatioEnabled Preference<Boolean> pixelRatioEnabled; @Inject @ScalpelEnabled Preference<Boolean> scalpelEnabled; @Inject @ScalpelWireframeEnabled Preference<Boolean> scalpelWireframeEnabled; @Inject NetworkBehavior behavior; @Inject @NetworkDelay Preference<Long> networkDelay; @Inject @NetworkFailurePercent Preference<Integer> networkFailurePercent; @Inject @NetworkVariancePercent Preference<Integer> networkVariancePercent; @Inject MockGithubService mockGithubService; @Inject Application app; @Inject Set<DebugAction> debugActions; private final ContextualDebugActions contextualDebugActions; public DebugView(Context context) { this(context, null); } public DebugView(Context context, AttributeSet attrs) { super(context, attrs); Injector.obtain(context).inject(this); // Inflate all of the controls and inject them. LayoutInflater.from(context).inflate(R.layout.debug_view_content, this); ButterKnife.bind(this); contextualDebugActions = new ContextualDebugActions(this, debugActions); setupNetworkSection(); setupMockBehaviorSection(); setupUserInterfaceSection(); setupBuildSection(); setupDeviceSection(); setupPicassoSection(); setupOkHttpCacheSection(); } public ContextualDebugActions getContextualDebugActions() { return contextualDebugActions; } public void onDrawerOpened() { refreshPicassoStats(); refreshOkHttpCacheStats(); } private void setupNetworkSection() { final ApiEndpoints currentEndpoint = ApiEndpoints.from(networkEndpoint.get()); final EnumAdapter<ApiEndpoints> endpointAdapter = new EnumAdapter<>(getContext(), ApiEndpoints.class); endpointView.setAdapter(endpointAdapter); endpointView.setSelection(currentEndpoint.ordinal()); RxAdapterView.itemSelections(endpointView) .map(endpointAdapter::getItem) .filter(item -> item != currentEndpoint) .subscribe( selected -> { if (selected == ApiEndpoints.CUSTOM) { Timber.d("Custom network endpoint selected. Prompting for URL."); showCustomEndpointDialog(currentEndpoint.ordinal(), "http://"); } else { setEndpointAndRelaunch(selected.url); } }); final NetworkDelayAdapter delayAdapter = new NetworkDelayAdapter(getContext()); networkDelayView.setAdapter(delayAdapter); networkDelayView.setSelection( NetworkDelayAdapter.getPositionForValue(behavior.delay(MILLISECONDS))); RxAdapterView.itemSelections(networkDelayView) .map(delayAdapter::getItem) .filter(item -> item != behavior.delay(MILLISECONDS)) .subscribe( selected -> { Timber.d("Setting network delay to %sms", selected); behavior.setDelay(selected, MILLISECONDS); networkDelay.set(selected); }); final NetworkVarianceAdapter varianceAdapter = new NetworkVarianceAdapter(getContext()); networkVarianceView.setAdapter(varianceAdapter); networkVarianceView.setSelection( NetworkVarianceAdapter.getPositionForValue(behavior.variancePercent())); RxAdapterView.itemSelections(networkVarianceView) .map(varianceAdapter::getItem) .filter(item -> item != behavior.variancePercent()) .subscribe( selected -> { Timber.d("Setting network variance to %s%%", selected); behavior.setVariancePercent(selected); networkVariancePercent.set(selected); }); final NetworkErrorAdapter errorAdapter = new NetworkErrorAdapter(getContext()); networkErrorView.setAdapter(errorAdapter); networkErrorView.setSelection( NetworkErrorAdapter.getPositionForValue(behavior.failurePercent())); RxAdapterView.itemSelections(networkErrorView) .map(errorAdapter::getItem) .filter(item -> item != behavior.failurePercent()) .subscribe( selected -> { Timber.d("Setting network error to %s%%", selected); behavior.setFailurePercent(selected); networkFailurePercent.set(selected); }); int currentProxyPosition = networkProxyAddress.isSet() ? ProxyAdapter.PROXY : ProxyAdapter.NONE; final ProxyAdapter proxyAdapter = new ProxyAdapter(getContext(), networkProxyAddress); networkProxyView.setAdapter(proxyAdapter); networkProxyView.setSelection(currentProxyPosition); RxAdapterView.itemSelections(networkProxyView) .filter(position -> !networkProxyAddress.isSet() || position != ProxyAdapter.PROXY) .subscribe( position -> { if (position == ProxyAdapter.NONE) { Timber.d("Clearing network proxy"); // TODO: Keep the custom proxy around so you can easily switch back and forth. networkProxyAddress.delete(); client.setProxy(null); apiClient.setProxy(null); } else if (networkProxyAddress.isSet() && position == ProxyAdapter.PROXY) { Timber.d("Ignoring re-selection of network proxy %s", networkProxyAddress.get()); } else { Timber.d("New network proxy selected. Prompting for host."); showNewNetworkProxyDialog(proxyAdapter); } }); // Only show the endpoint editor when a custom endpoint is in use. endpointEditView.setVisibility(currentEndpoint == ApiEndpoints.CUSTOM ? VISIBLE : GONE); if (currentEndpoint == ApiEndpoints.MOCK_MODE) { // Disable network proxy if we are in mock mode. networkProxyView.setEnabled(false); networkLoggingView.setEnabled(false); } else { // Disable network controls if we are not in mock mode. networkDelayView.setEnabled(false); networkVarianceView.setEnabled(false); networkErrorView.setEnabled(false); } // We use the JSON rest adapter as the source of truth for the log level. // final EnumAdapter<RestAdapter.LogLevel> loggingAdapter = // new EnumAdapter<>(getContext(), RestAdapter.LogLevel.class); // networkLoggingView.setAdapter(loggingAdapter); // networkLoggingView.setSelection(retrofit.getLogLevel().ordinal()); // networkLoggingView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { // @Override // public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) { // RestAdapter.LogLevel selected = loggingAdapter.getItem(position); // if (selected != retrofit.getLogLevel()) { // Timber.d("Setting logging level to %s", selected); // retrofit.setLogLevel(selected); // } else { // Timber.d("Ignoring re-selection of logging level " + selected); // } // } // // @Override public void onNothingSelected(AdapterView<?> adapterView) { // } // }); } @OnClick(R.id.debug_network_endpoint_edit) void onEditEndpointClicked() { Timber.d("Prompting to edit custom endpoint URL."); // Pass in the currently selected position since we are merely editing. showCustomEndpointDialog(endpointView.getSelectedItemPosition(), networkEndpoint.get()); } private void setupMockBehaviorSection() { captureIntentsView.setEnabled(isMockMode); captureIntentsView.setChecked(captureIntents.get()); captureIntentsView.setOnCheckedChangeListener( (compoundButton, b) -> { Timber.d("Capture intents set to %s", b); captureIntents.set(b); }); configureResponseSpinner(repositoriesResponseView, MockRepositoriesResponse.class); } /** * Populates a {@code Spinner} with the values of an {@code enum} and binds it to the value set in * the mock service. */ private <T extends Enum<T>> void configureResponseSpinner( Spinner spinner, final Class<T> responseClass) { final EnumAdapter<T> adapter = new EnumAdapter<>(getContext(), responseClass); spinner.setEnabled(isMockMode); spinner.setAdapter(adapter); spinner.setSelection(mockGithubService.getResponse(responseClass).ordinal()); RxAdapterView.itemSelections(spinner) .map(adapter::getItem) .filter(item -> item != mockGithubService.getResponse(responseClass)) .subscribe( selected -> { Timber.d("Setting %s to %s", responseClass.getSimpleName(), selected); mockGithubService.setResponse(responseClass, selected); }); } private void setupUserInterfaceSection() { final AnimationSpeedAdapter speedAdapter = new AnimationSpeedAdapter(getContext()); uiAnimationSpeedView.setAdapter(speedAdapter); final int animationSpeedValue = animationSpeed.get(); uiAnimationSpeedView.setSelection( AnimationSpeedAdapter.getPositionForValue(animationSpeedValue)); RxAdapterView.itemSelections(uiAnimationSpeedView) .map(speedAdapter::getItem) .filter(item -> item != animationSpeed.get()) .subscribe( selected -> { Timber.d("Setting animation speed to %sx", selected); animationSpeed.set(selected); applyAnimationSpeed(selected); }); // Ensure the animation speed value is always applied across app restarts. post(() -> applyAnimationSpeed(animationSpeedValue)); boolean gridEnabled = pixelGridEnabled.get(); uiPixelGridView.setChecked(gridEnabled); uiPixelRatioView.setEnabled(gridEnabled); uiPixelGridView.setOnCheckedChangeListener( (buttonView, isChecked) -> { Timber.d("Setting pixel grid overlay enabled to " + isChecked); pixelGridEnabled.set(isChecked); uiPixelRatioView.setEnabled(isChecked); }); uiPixelRatioView.setChecked(pixelRatioEnabled.get()); uiPixelRatioView.setOnCheckedChangeListener( (buttonView, isChecked) -> { Timber.d("Setting pixel scale overlay enabled to " + isChecked); pixelRatioEnabled.set(isChecked); }); uiScalpelView.setChecked(scalpelEnabled.get()); uiScalpelWireframeView.setEnabled(scalpelEnabled.get()); uiScalpelView.setOnCheckedChangeListener( (buttonView, isChecked) -> { Timber.d("Setting scalpel interaction enabled to " + isChecked); scalpelEnabled.set(isChecked); uiScalpelWireframeView.setEnabled(isChecked); }); uiScalpelWireframeView.setChecked(scalpelWireframeEnabled.get()); uiScalpelWireframeView.setOnCheckedChangeListener( (buttonView, isChecked) -> { Timber.d("Setting scalpel wireframe enabled to " + isChecked); scalpelWireframeEnabled.set(isChecked); }); } @OnClick(R.id.debug_logs_show) void showLogs() { new LogsDialog(new ContextThemeWrapper(getContext(), R.style.Theme_U2020), lumberYard).show(); } @OnClick(R.id.debug_leaks_show) void showLeaks() { Intent intent = new Intent(getContext(), DisplayLeakActivity.class); getContext().startActivity(intent); } private void setupBuildSection() { buildNameView.setText(BuildConfig.VERSION_NAME); buildCodeView.setText(String.valueOf(BuildConfig.VERSION_CODE)); buildShaView.setText(BuildConfig.GIT_SHA); TemporalAccessor buildTime = ISO_INSTANT.parse(BuildConfig.BUILD_TIME); buildDateView.setText(DATE_DISPLAY_FORMAT.format(buildTime)); } private void setupDeviceSection() { DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics(); String densityBucket = getDensityString(displayMetrics); deviceMakeView.setText(Strings.truncateAt(Build.MANUFACTURER, 20)); deviceModelView.setText(Strings.truncateAt(Build.MODEL, 20)); deviceResolutionView.setText(displayMetrics.heightPixels + "x" + displayMetrics.widthPixels); deviceDensityView.setText(displayMetrics.densityDpi + "dpi (" + densityBucket + ")"); deviceReleaseView.setText(Build.VERSION.RELEASE); deviceApiView.setText(String.valueOf(Build.VERSION.SDK_INT)); } private void setupPicassoSection() { boolean picassoDebuggingValue = picassoDebugging.get(); picasso.setIndicatorsEnabled(picassoDebuggingValue); picassoIndicatorView.setChecked(picassoDebuggingValue); picassoIndicatorView.setOnCheckedChangeListener( (button, isChecked) -> { Timber.d("Setting Picasso debugging to " + isChecked); picasso.setIndicatorsEnabled(isChecked); picassoDebugging.set(isChecked); }); refreshPicassoStats(); } private void refreshPicassoStats() { StatsSnapshot snapshot = picasso.getSnapshot(); String size = getSizeString(snapshot.size); String total = getSizeString(snapshot.maxSize); int percentage = (int) ((1f * snapshot.size / snapshot.maxSize) * 100); picassoCacheSizeView.setText(size + " / " + total + " (" + percentage + "%)"); picassoCacheHitView.setText(String.valueOf(snapshot.cacheHits)); picassoCacheMissView.setText(String.valueOf(snapshot.cacheMisses)); picassoDecodedView.setText(String.valueOf(snapshot.originalBitmapCount)); picassoDecodedTotalView.setText(getSizeString(snapshot.totalOriginalBitmapSize)); picassoDecodedAvgView.setText(getSizeString(snapshot.averageOriginalBitmapSize)); picassoTransformedView.setText(String.valueOf(snapshot.transformedBitmapCount)); picassoTransformedTotalView.setText(getSizeString(snapshot.totalTransformedBitmapSize)); picassoTransformedAvgView.setText(getSizeString(snapshot.averageTransformedBitmapSize)); } private void setupOkHttpCacheSection() { Cache cache = client.getCache(); // Shares the cache with apiClient, so no need to check both. okHttpCacheMaxSizeView.setText(getSizeString(cache.getMaxSize())); refreshOkHttpCacheStats(); } private void refreshOkHttpCacheStats() { Cache cache = client.getCache(); // Shares the cache with apiClient, so no need to check both. int writeTotal = cache.getWriteSuccessCount() + cache.getWriteAbortCount(); int percentage = (int) ((1f * cache.getWriteAbortCount() / writeTotal) * 100); okHttpCacheWriteErrorView.setText( cache.getWriteAbortCount() + " / " + writeTotal + " (" + percentage + "%)"); okHttpCacheRequestCountView.setText(String.valueOf(cache.getRequestCount())); okHttpCacheNetworkCountView.setText(String.valueOf(cache.getNetworkCount())); okHttpCacheHitCountView.setText(String.valueOf(cache.getHitCount())); } private void applyAnimationSpeed(int multiplier) { try { Method method = ValueAnimator.class.getDeclaredMethod("setDurationScale", float.class); method.invoke(null, (float) multiplier); } catch (Exception e) { throw new RuntimeException("Unable to apply animation speed.", e); } } private static String getDensityString(DisplayMetrics displayMetrics) { switch (displayMetrics.densityDpi) { case DisplayMetrics.DENSITY_LOW: return "ldpi"; case DisplayMetrics.DENSITY_MEDIUM: return "mdpi"; case DisplayMetrics.DENSITY_HIGH: return "hdpi"; case DisplayMetrics.DENSITY_XHIGH: return "xhdpi"; case DisplayMetrics.DENSITY_XXHIGH: return "xxhdpi"; case DisplayMetrics.DENSITY_XXXHIGH: return "xxxhdpi"; case DisplayMetrics.DENSITY_TV: return "tvdpi"; default: return String.valueOf(displayMetrics.densityDpi); } } private static String getSizeString(long bytes) { String[] units = new String[] {"B", "KB", "MB", "GB"}; int unit = 0; while (bytes >= 1024) { bytes /= 1024; unit += 1; } return bytes + units[unit]; } private void showNewNetworkProxyDialog(final ProxyAdapter proxyAdapter) { final int originalSelection = networkProxyAddress.isSet() ? ProxyAdapter.PROXY : ProxyAdapter.NONE; View view = LayoutInflater.from(app).inflate(R.layout.debug_drawer_network_proxy, null); final EditText hostView = findById(view, R.id.debug_drawer_network_proxy_host); if (networkProxyAddress.isSet()) { String host = networkProxyAddress.get().getHostName(); hostView.setText(host); // Set the current host. hostView.setSelection(0, host.length()); // Pre-select it for editing. // Show the keyboard. Post this to the next frame when the dialog has been attached. hostView.post(() -> Keyboards.showKeyboard(hostView)); } new AlertDialog.Builder(getContext()) // .setTitle("Set Network Proxy") .setView(view) .setNegativeButton( "Cancel", (dialog, i) -> { networkProxyView.setSelection(originalSelection); dialog.cancel(); }) .setPositiveButton( "Use", (dialog, i) -> { String in = hostView.getText().toString(); InetSocketAddress address = InetSocketAddressPreferenceAdapter.parse(in); if (address != null) { networkProxyAddress.set(address); // Persist across restarts. proxyAdapter.notifyDataSetChanged(); // Tell the spinner to update. networkProxyView.setSelection(ProxyAdapter.PROXY); // And show the proxy. Proxy proxy = InetSocketAddressPreferenceAdapter.createProxy(address); client.setProxy(proxy); apiClient.setProxy(proxy); } else { networkProxyView.setSelection(originalSelection); } }) .setOnCancelListener(dialogInterface -> networkProxyView.setSelection(originalSelection)) .show(); } private void showCustomEndpointDialog(final int originalSelection, String defaultUrl) { View view = LayoutInflater.from(app).inflate(R.layout.debug_drawer_network_endpoint, null); final EditText url = findById(view, R.id.debug_drawer_network_endpoint_url); url.setText(defaultUrl); url.setSelection(url.length()); new AlertDialog.Builder(getContext()) // .setTitle("Set Network Endpoint") .setView(view) .setNegativeButton( "Cancel", (dialog, i) -> { endpointView.setSelection(originalSelection); dialog.cancel(); }) .setPositiveButton( "Use", (dialog, i) -> { String theUrl = url.getText().toString(); if (!Strings.isBlank(theUrl)) { setEndpointAndRelaunch(theUrl); } else { endpointView.setSelection(originalSelection); } }) .setOnCancelListener( (dialogInterface) -> { endpointView.setSelection(originalSelection); }) .show(); } private void setEndpointAndRelaunch(String endpoint) { Timber.d("Setting network endpoint to %s", endpoint); networkEndpoint.set(endpoint); ProcessPhoenix.triggerRebirth(getContext()); } }
/** Provides ExternalIds for equity future options used to build a volatility surface */ public class BloombergEquityFutureOptionVolatilitySurfaceInstrumentProvider extends BloombergFutureOptionVolatilitySurfaceInstrumentProvider { /** The logger */ private static final Logger s_logger = LoggerFactory.getLogger(BloombergEquityFutureOptionVolatilitySurfaceInstrumentProvider.class); /** The date-time formatter */ private static final DateTimeFormatter FORMAT = DateTimeFormatter.ofPattern("MM/dd/yy"); /** The expiry rules */ private static final HashMap<String, ExchangeTradedInstrumentExpiryCalculator> EXPIRY_RULES; /** An empty holiday calendar */ private static final Calendar NO_HOLIDAYS = new NoHolidayCalendar(); static { EXPIRY_RULES = new HashMap<>(); EXPIRY_RULES.put("NKY", FutureOptionExpiries.of(new NextExpiryAdjuster(2, DayOfWeek.FRIDAY))); EXPIRY_RULES.put( "NDX", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); // TODO EXPIRY_RULES.put( "RUT", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); // TODO EXPIRY_RULES.put( "DJX", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); EXPIRY_RULES.put( "SPX", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); EXPIRY_RULES.put( "VIX", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, -30))); EXPIRY_RULES.put( "AAPL US", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); EXPIRY_RULES.put("UKX", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY))); EXPIRY_RULES.put( "FB US", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); EXPIRY_RULES.put( "TWSE", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.WEDNESDAY))); EXPIRY_RULES.put("HSCEI", new DaysFromEndOfMonthExpiryAdjuster(1)); EXPIRY_RULES.put( "RDXUSD", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.THURSDAY, 1))); EXPIRY_RULES.put( "DEFAULT", FutureOptionExpiries.of(new NextExpiryAdjuster(3, DayOfWeek.FRIDAY, 1))); // TODO DAX, EUROSTOXX 50 (SX5E) } /** * Uses the default ticker scheme (BLOOMBERG_TICKER_WEAK) * * @param futureOptionPrefix the prefix to the resulting code (e.g. DJX), not null * @param postfix the postfix to the resulting code (e.g. Index), not null * @param dataFieldName the name of the data field, not null. Expecting * MarketDataRequirementNames.IMPLIED_VOLATILITY or OPT_IMPLIED_VOLATILITY_MID * @param useCallAboveStrike the strike above which to use calls rather than puts, not null * @param exchangeIdName the exchange id, not null */ public BloombergEquityFutureOptionVolatilitySurfaceInstrumentProvider( final String futureOptionPrefix, final String postfix, final String dataFieldName, final Double useCallAboveStrike, final String exchangeIdName) { super(futureOptionPrefix, postfix, dataFieldName, useCallAboveStrike, exchangeIdName); } /** * @param futureOptionPrefix the prefix to the resulting code (e.g. DJX), not null * @param postfix the postfix to the resulting code (e.g. Index), not null * @param dataFieldName the name of the data field, not null. Expecting * MarketDataRequirementNames.IMPLIED_VOLATILITY or OPT_IMPLIED_VOLATILITY_MID * @param useCallAboveStrike the strike above which to use calls rather than puts, not null * @param exchangeIdName the exchange id, not null * @param schemeName the ticker scheme name, not null */ public BloombergEquityFutureOptionVolatilitySurfaceInstrumentProvider( final String futureOptionPrefix, final String postfix, final String dataFieldName, final Double useCallAboveStrike, final String exchangeIdName, final String schemeName) { super( futureOptionPrefix, postfix, dataFieldName, useCallAboveStrike, exchangeIdName, schemeName); } /** * Provides an ExternalID for Bloomberg ticker, given a reference date and an integer offset, the * n'th subsequent option * * <p>The format is prefix + date + callPutFlag + strike + postfix * * <p>e.g. DJX 12/21/13 C145.0 Index * * <p> * * @param futureOptionNumber n'th future following curve date, not null * @param strike option's strike, expressed as price in %, e.g. 98.750, not null * @param surfaceDate date of curve validity; valuation date, not null * @return the id of the Bloomberg ticker */ @Override public ExternalId getInstrument( final Number futureOptionNumber, final Double strike, final LocalDate surfaceDate) { ArgumentChecker.notNull(futureOptionNumber, "futureOptionNumber"); ArgumentChecker.notNull(strike, "strike"); ArgumentChecker.notNull(surfaceDate, "surface date"); final String prefix = getFutureOptionPrefix(); final StringBuffer ticker = new StringBuffer(); ticker.append(prefix); ticker.append(" "); final ExchangeTradedInstrumentExpiryCalculator expiryRule = getExpiryRuleCalculator(); final LocalDate expiry = expiryRule.getExpiryDate(futureOptionNumber.intValue(), surfaceDate, NO_HOLIDAYS); ticker.append(FORMAT.format(expiry)); ticker.append(" "); ticker.append(strike > useCallAboveStrike() ? "C" : "P"); ticker.append(strike); ticker.append(" "); ticker.append(getPostfix()); return ExternalId.of(getScheme(), ticker.toString()); } /** * Gets the expiryRules. * * @return the expiryRules */ public static HashMap<String, ExchangeTradedInstrumentExpiryCalculator> getExpiryRules() { return EXPIRY_RULES; } @Override public ExchangeTradedInstrumentExpiryCalculator getExpiryRuleCalculator() { final String prefix = getFutureOptionPrefix(); ExchangeTradedInstrumentExpiryCalculator expiryRule = EXPIRY_RULES.get(prefix); if (expiryRule == null) { s_logger.info( "No expiry rule has been setup for " + prefix + ". Using Default of 3rd Friday."); expiryRule = EXPIRY_RULES.get("DEFAULT"); } return expiryRule; } }