/* * Copyright (c) 2004-2011 Marco Maccaferri and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Marco Maccaferri - initial API and implementation */ package org.eclipsetrader.core.internal.markets; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TimeZone; import java.util.TreeSet; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementRef; import javax.xml.bind.annotation.XmlElementWrapper; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; import org.eclipse.core.runtime.PlatformObject; import org.eclipsetrader.core.feed.IBackfillConnector; import org.eclipsetrader.core.feed.IFeedConnector; import org.eclipsetrader.core.instruments.ISecurity; import org.eclipsetrader.core.markets.IMarket; import org.eclipsetrader.core.markets.IMarketDay; @XmlRootElement(name = "market") @XmlType(name = "org.eclipsetrader.core.markets.Market") public class Market extends PlatformObject implements IMarket { @XmlAttribute(name = "name") private String name; @XmlAttribute(name = "timeZone") @XmlJavaTypeAdapter(TimeZoneAdapter.class) private TimeZone timeZone; @XmlAttribute(name = "weekDays") @XmlJavaTypeAdapter(WeekdaysAdapter.class) private Set<Integer> weekDays; @XmlElement(name = "liveFeed") private MarketConnector liveFeedConnector; @XmlElement(name = "backfill") private MarketBackfillConnector backfillConnector; @XmlElement(name = "intraday-backfill") private MarketBackfillConnector intradayBackfillConnector; @XmlElementWrapper(name = "schedule") @XmlElementRef private SortedSet<MarketTime> schedule; @XmlElementWrapper(name = "holidays") @XmlElementRef private SortedSet<MarketHoliday> holidays; @XmlElementWrapper(name = "members") @XmlElement(name = "security") @XmlJavaTypeAdapter(SecurityAdapter.class) private List<ISecurity> members; private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); protected Market() { } public Market(String name, Collection<MarketTime> schedule) { this(name, schedule, null); } public Market(String name, Collection<MarketTime> schedule, TimeZone timeZone) { this.name = name; this.schedule = schedule != null ? new TreeSet<MarketTime>(schedule) : null; this.timeZone = timeZone; } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#getName() */ @Override @XmlTransient public String getName() { return name; } public void setName(String name) { Object oldValue = this.name; this.name = name; propertyChangeSupport.firePropertyChange(PROP_NAME, oldValue, this.name); } @XmlTransient public MarketTime[] getSchedule() { return schedule.toArray(new MarketTime[schedule.size()]); } public void setSchedule(MarketTime[] schedule) { Object oldValue = this.schedule.toArray(new MarketTime[this.schedule.size()]); this.schedule = schedule != null ? new TreeSet<MarketTime>(Arrays.asList(schedule)) : null; propertyChangeSupport.firePropertyChange(PROP_SCHEDULE, oldValue, this.schedule.toArray(new MarketTime[this.schedule.size()])); } @XmlTransient public Integer[] getWeekDays() { if (weekDays == null) { return new Integer[0]; } return weekDays.toArray(new Integer[weekDays.size()]); } public void setWeekDays(Integer[] weekDays) { Object oldValue = this.weekDays; this.weekDays = new HashSet<Integer>(Arrays.asList(weekDays)); propertyChangeSupport.firePropertyChange(PROP_WEEKDAYS, oldValue, this.weekDays); } @XmlTransient public MarketHoliday[] getHolidays() { if (holidays == null) { return new MarketHoliday[0]; } return holidays.toArray(new MarketHoliday[holidays.size()]); } public void setHolidays(MarketHoliday[] holidays) { Object oldValue = this.holidays; this.holidays = new TreeSet<MarketHoliday>(Arrays.asList(holidays)); propertyChangeSupport.firePropertyChange(PROP_HOLIDAYS, oldValue, this.holidays); } @XmlTransient public TimeZone getTimeZone() { return timeZone; } public void setTimeZone(TimeZone timeZone) { Object oldValue = this.timeZone; this.timeZone = timeZone; propertyChangeSupport.firePropertyChange(PROP_TIMEZONE, oldValue, this.timeZone); } /* (non-Javadoc) * @see org.eclipsetrader.core.model.markets.IMarket#isOpen() */ @Override public boolean isOpen() { return isOpen(Calendar.getInstance().getTime()); } /* (non-Javadoc) * @see org.eclipsetrader.core.model.markets.IMarket#isOpen(java.util.Date) */ @Override public boolean isOpen(Date time) { MarketDay day = getMarketDayFor(time); return day.isOpen(time); } protected Date getCombinedDateTime(Date date, Date time) { Calendar normalized = Calendar.getInstance(timeZone != null ? timeZone : TimeZone.getDefault()); Calendar refTime = Calendar.getInstance(); refTime.setTime(date); normalized.set(Calendar.YEAR, refTime.get(Calendar.YEAR)); normalized.set(Calendar.MONTH, refTime.get(Calendar.MONTH)); normalized.set(Calendar.DAY_OF_MONTH, refTime.get(Calendar.DAY_OF_MONTH)); refTime = Calendar.getInstance(); refTime.setTime(time); normalized.set(Calendar.HOUR_OF_DAY, refTime.get(Calendar.HOUR_OF_DAY)); normalized.set(Calendar.MINUTE, refTime.get(Calendar.MINUTE)); normalized.set(Calendar.SECOND, 0); normalized.set(Calendar.MILLISECOND, 0); return normalized.getTime(); } protected String getHolidayDescriptionFor(Date time) { Calendar calendar = Calendar.getInstance(); calendar.setTime(time); calendar.set(Calendar.MILLISECOND, 0); if (holidays != null) { Calendar holiday = Calendar.getInstance(); for (MarketHoliday day : holidays) { holiday.setTime(day.getDate()); if (calendar.get(Calendar.YEAR) == holiday.get(Calendar.YEAR) && calendar.get(Calendar.MONTH) == holiday.get(Calendar.MONTH) && calendar.get(Calendar.DAY_OF_MONTH) == holiday.get(Calendar.DAY_OF_MONTH)) { return day.getDescription(); } } } return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#getToday() */ @Override public IMarketDay getToday() { return getMarketDayFor(Calendar.getInstance().getTime()); } protected MarketDay getMarketDayFor(Date time) { Calendar calendar = Calendar.getInstance(); calendar.setTime(time); calendar.set(Calendar.MILLISECOND, 0); if (holidays != null) { Calendar holiday = Calendar.getInstance(); for (MarketHoliday day : holidays) { holiday.setTime(day.getDate()); if (calendar.get(Calendar.YEAR) == holiday.get(Calendar.YEAR) && calendar.get(Calendar.MONTH) == holiday.get(Calendar.MONTH) && calendar.get(Calendar.DAY_OF_MONTH) == holiday.get(Calendar.DAY_OF_MONTH)) { if (day.getOpenTime() == null || day.getCloseTime() == null) { return new MarketDay(null, null, day.getDescription()); } return new MarketDay(getCombinedDateTime(day.getOpenTime(), day.getOpenTime()), getCombinedDateTime(day.getCloseTime(), day.getCloseTime()), day.getDescription()); } } } if (weekDays != null) { if (!weekDays.contains(calendar.get(Calendar.DAY_OF_WEEK))) { return new MarketDay(null, null, null); } } if (schedule != null) { for (MarketTime marketTime : schedule) { Date openTime = getCombinedDateTime(time, marketTime.getOpenTime()); Date closeTime = getCombinedDateTime(time, marketTime.getCloseTime()); if (!marketTime.isExcluded(time) && (time.equals(openTime) || time.after(openTime)) && time.before(closeTime)) { return new MarketDay(openTime, closeTime, marketTime.getDescription()); } } for (MarketTime marketTime : schedule) { Date openTime = getCombinedDateTime(time, marketTime.getOpenTime()); Date closeTime = getCombinedDateTime(time, marketTime.getCloseTime()); if (openTime.equals(time) || openTime.after(time)) { return new MarketDay(openTime, closeTime, null); } } } return new MarketDay(null, null, null); } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#getNextDay() */ @Override public IMarketDay getNextDay() { return getNextMarketDayFor(Calendar.getInstance().getTime()); } protected MarketDay getNextMarketDayFor(Date time) { Calendar calendar = Calendar.getInstance(timeZone != null ? timeZone : TimeZone.getDefault()); calendar.setTime(time); calendar.set(Calendar.MILLISECOND, 0); MarketDay day = getMarketDayFor(time); if (day.getOpenTime() != null && time.before(day.getOpenTime())) { return day; } if (schedule != null && schedule.size() != 0) { Calendar refTime = Calendar.getInstance(); refTime.setTime(schedule.first().getOpenTime()); calendar.set(Calendar.HOUR_OF_DAY, refTime.get(Calendar.HOUR_OF_DAY)); calendar.set(Calendar.MINUTE, refTime.get(Calendar.MINUTE)); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); for (int i = 0; i < 7; i++) { calendar.add(Calendar.DATE, 1); day = getMarketDayFor(calendar.getTime()); if (day.isOpen(calendar.getTime())) { return day; } } } return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#addMember(org.eclipsetrader.core.instruments.ISecurity[]) */ @Override public void addMembers(ISecurity[] securities) { if (members == null) { members = new ArrayList<ISecurity>(); } Object oldValue = getMembers(); for (ISecurity security : securities) { if (!members.contains(security)) { members.add(security); } } propertyChangeSupport.firePropertyChange(PROP_MEMBERS, oldValue, getMembers()); } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#removeMember(org.eclipsetrader.core.instruments.ISecurity[]) */ @Override public void removeMembers(ISecurity[] securities) { if (members != null) { Object oldValue = getMembers(); members.removeAll(Arrays.asList(securities)); propertyChangeSupport.firePropertyChange(PROP_MEMBERS, oldValue, getMembers()); } } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#getMembers() */ @Override @XmlTransient public ISecurity[] getMembers() { if (members == null) { return new ISecurity[0]; } return members.toArray(new ISecurity[members.size()]); } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#hasMember(org.eclipsetrader.core.instruments.ISecurity) */ @Override public boolean hasMember(ISecurity security) { return members != null && members.contains(security); } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#getLiveFeedConnector() */ @Override @XmlTransient public IFeedConnector getLiveFeedConnector() { return liveFeedConnector != null ? liveFeedConnector.getConnector() : null; } public void setLiveFeedConnector(IFeedConnector liveFeedConnector) { Object oldValue = this.liveFeedConnector != null ? this.liveFeedConnector.getConnector() : null; this.liveFeedConnector = liveFeedConnector != null ? new MarketConnector(liveFeedConnector) : null; propertyChangeSupport.firePropertyChange(PROP_LIVE_FEED_CONNECTOR, oldValue, this.liveFeedConnector != null ? this.liveFeedConnector.getConnector() : null); } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#getBackfillConnector() */ @Override @XmlTransient public IBackfillConnector getBackfillConnector() { return backfillConnector != null ? backfillConnector.getConnector() : null; } public void setBackfillConnector(IBackfillConnector backfillConnector) { this.backfillConnector = backfillConnector != null ? new MarketBackfillConnector(backfillConnector) : null; } /* (non-Javadoc) * @see org.eclipsetrader.core.markets.IMarket#getIntradayBackfillConnector() */ @Override @XmlTransient public IBackfillConnector getIntradayBackfillConnector() { return intradayBackfillConnector != null ? intradayBackfillConnector.getConnector() : null; } public void setIntradayBackfillConnector(IBackfillConnector intradayBackfillConnector) { this.intradayBackfillConnector = intradayBackfillConnector != null ? new MarketBackfillConnector(intradayBackfillConnector) : null; } /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return 7 * name.hashCode(); } /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { return name; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @Override @SuppressWarnings("unchecked") public Object getAdapter(Class adapter) { if (adapter.isAssignableFrom(PropertyChangeSupport.class)) { return propertyChangeSupport; } if (adapter.isAssignableFrom(getClass())) { return this; } return super.getAdapter(adapter); } }