melphi/algobox

View on GitHub
java/algobox/algobox-core/src/main/java/io/algobox/util/MarketHoursUtils.java

Summary

Maintainability
A
3 hrs
Test Coverage
package io.algobox.util;

import io.algobox.instrument.InstrumentInfoDetailed;
import io.algobox.instrument.MarketHours;

import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Objects;
import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;

public final class MarketHoursUtils {
  public static Optional<MarketHours> getMarketHours(
      InstrumentInfoDetailed info, long timestampUtc) {
    ZonedDateTime localDateTime = DateTimeUtils.getDateTime(timestampUtc)
        .withZoneSameInstant(ZoneId.of(info.getTimeZoneId()));
    if (Boolean.TRUE.equals(info.getIs24hMarket())) {
      return MarketHoursUtils.getMarketHours24HoursMarket(info, localDateTime);
    } else if(Boolean.FALSE.equals(info.getIs24hMarket())) {
      return MarketHoursUtils.getMarketHoursLocalMarket(info, localDateTime);
    } else {
      throw new IllegalArgumentException("Missing value is24hMarket");
    }
  }

  /**
   * A local market, eg DAX index, starts and ends the same day in the local time.
   */
  public static Optional<MarketHours> getMarketHoursLocalMarket(
      InstrumentInfoDetailed info, ZonedDateTime localDateTime) {
    checkArgument(localDateTime.getZone().getId().equals(info.getTimeZoneId()));
    ZonedDateTime previousDayDateTime = localDateTime.minusDays(1);
    switch (localDateTime.getDayOfWeek()) {
      case MONDAY:
        previousDayDateTime = localDateTime.minusDays(3);
      case TUESDAY:
      case WEDNESDAY:
      case THURSDAY:
      case FRIDAY:
        ZonedDateTime marketOpen = ZonedDateTime.of(localDateTime.getYear(),
            localDateTime.getMonth().getValue(), localDateTime.getDayOfMonth(), info.getOpenHour(),
            info.getOpenMinute(), 0, 0, localDateTime.getZone());
        ZonedDateTime marketClose = ZonedDateTime.of(localDateTime.getYear(),
            localDateTime.getMonth().getValue(), localDateTime.getDayOfMonth(), info.getCloseHour(),
            info.getCloseMinute(), 0, 0, localDateTime.getZone());
        ZonedDateTime orb5minOpen = ZonedDateTime.of(localDateTime.getYear(),
            localDateTime.getMonth().getValue(), localDateTime.getDayOfMonth(),
            info.getOrb5MinOpenHour(), 0, 0, 0, localDateTime.getZone());
        ZonedDateTime previousMarketOpen = ZonedDateTime.of(previousDayDateTime.getYear(),
            previousDayDateTime.getMonth().getValue(), previousDayDateTime.getDayOfMonth(),
            info.getOpenHour(), info.getOpenMinute(), 0, 0, previousDayDateTime.getZone());
        ZonedDateTime previousMarketClose = ZonedDateTime.of(previousDayDateTime.getYear(),
            previousDayDateTime.getMonth().getValue(), previousDayDateTime.getDayOfMonth(),
            info.getCloseHour(), info.getCloseMinute(), 0, 0, localDateTime.getZone());
        return Optional.of(new MarketHours(
            DateTimeUtils.getUtcMilliseconds(marketOpen),
            DateTimeUtils.getUtcMilliseconds(marketClose),
            DateTimeUtils.getUtcMilliseconds(orb5minOpen),
            DateTimeUtils.getUtcMilliseconds(getOrb5MinClose(orb5minOpen)),
            DateTimeUtils.getUtcMilliseconds(previousMarketOpen),
            DateTimeUtils.getUtcMilliseconds(previousMarketClose)));
      case SATURDAY:
      case SUNDAY:
        return Optional.empty();
      default:
        throw new IllegalArgumentException(String.format("Unexpected day [%s] for [%s].",
            localDateTime.getDayOfWeek(), localDateTime.toString()));
    }
  }

  /**
   * A 24 hours market, eg forex, starts at market close and ends immediately before market
   * close, remaining open during all night.
   */
  public static Optional<MarketHours> getMarketHours24HoursMarket(
      InstrumentInfoDetailed info, ZonedDateTime localDateTime) {
    checkArgument(localDateTime.getZone().getId().equals(info.getTimeZoneId()));
    checkArgument(Objects.equals(info.getOpenHour(), info.getCloseHour()) &&
            Objects.equals(info.getOpenMinute(), info.getCloseMinute()),
        "Market open and close should match in a 24 hours market.");
    ZonedDateTime marketOpen;
    ZonedDateTime orb5minOpen;
    ZonedDateTime previousMarketOpen;
    ZonedDateTime previousDayDateTime = localDateTime.minusDays(1);
    switch (localDateTime.getDayOfWeek()) {
      case MONDAY:
        previousDayDateTime = localDateTime.minusDays(3);
      case TUESDAY:
      case WEDNESDAY:
      case THURSDAY:
      case FRIDAY:
        marketOpen = ZonedDateTime.of(localDateTime.getYear(), localDateTime.getMonth().getValue(),
            localDateTime.getDayOfMonth(), info.getOpenHour(), info.getOpenMinute(), 0, 0,
            localDateTime.getZone()).minusDays(1);
        previousMarketOpen = ZonedDateTime.of(previousDayDateTime.getYear(),
            previousDayDateTime.getMonth().getValue(), previousDayDateTime.getDayOfMonth(),
            info.getOpenHour(), info.getOpenMinute(), 0, 0,
            previousDayDateTime.getZone()).minusDays(1);
        orb5minOpen = ZonedDateTime.of(localDateTime.getYear(), localDateTime.getMonth().getValue(),
            localDateTime.getDayOfMonth(), info.getOrb5MinOpenHour(), 0, 0, 0,
            localDateTime.getZone());
        break;
      case SATURDAY:
        return Optional.empty();
      case SUNDAY:
        previousDayDateTime = localDateTime.minusDays(2);
        marketOpen = ZonedDateTime.of(localDateTime.getYear(), localDateTime.getMonth().getValue(),
            localDateTime.getDayOfMonth(), info.getOpenHour(), info.getOpenMinute(), 0, 0,
            localDateTime.getZone());
        previousMarketOpen = ZonedDateTime.of(previousDayDateTime.getYear(),
            previousDayDateTime.getMonth().getValue(), previousDayDateTime.getDayOfMonth(),
            info.getOpenHour(), info.getOpenMinute(), 0, 0, previousDayDateTime.getZone())
            .minusDays(1);
        orb5minOpen = ZonedDateTime.of(localDateTime.getYear(), localDateTime.getMonth().getValue(),
            localDateTime.getDayOfMonth(), info.getOrb5MinOpenHour(), 0, 0, 0,
            localDateTime.getZone())
            .plusDays(1);
        break;
      default:
        throw new IllegalArgumentException(String.format("Unexpected day [%s] for [%s].",
            localDateTime.getDayOfWeek(), localDateTime));
    }
    return Optional.of(new MarketHours(
        DateTimeUtils.getUtcMilliseconds(marketOpen),
        DateTimeUtils.getUtcMilliseconds(marketOpen.plusDays(1)),
        DateTimeUtils.getUtcMilliseconds(orb5minOpen),
        DateTimeUtils.getUtcMilliseconds(getOrb5MinClose(orb5minOpen)),
        DateTimeUtils.getUtcMilliseconds(previousMarketOpen),
        DateTimeUtils.getUtcMilliseconds(previousMarketOpen.plusDays(1))));
  }

  private static ZonedDateTime getOrb5MinClose(ZonedDateTime orb5MinOpen) {
    return orb5MinOpen.plusMinutes(5);
  }
}