/* * 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.feed; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.eclipse.core.runtime.Assert; import org.eclipsetrader.core.instruments.ISecurity; import org.eclipsetrader.core.repositories.IPropertyConstants; import org.eclipsetrader.core.repositories.IRepository; import org.eclipsetrader.core.repositories.IStore; import org.eclipsetrader.core.repositories.IStoreObject; import org.eclipsetrader.core.repositories.IStoreProperties; import org.eclipsetrader.core.repositories.StoreProperties; public class HistoryDay implements IHistory { private ISecurity security; private IOHLC[] bars = new IOHLC[0]; private TimeSpan timeSpan; private IOHLC highest; private IOHLC lowest; private Map<Date, StoreObject> storeObjects = new TreeMap<Date, StoreObject>(); private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); private class StoreObject implements IStoreObject { private IStore store; private IStoreProperties storeProperties; public StoreObject(IStore store, IStoreProperties storeProperties) { this.store = store; this.storeProperties = storeProperties; } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IStoreObject#getStore() */ @Override public IStore getStore() { return store; } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IStoreObject#getStoreProperties() */ @Override public IStoreProperties getStoreProperties() { return storeProperties; } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IStoreObject#setStore(org.eclipsetrader.core.repositories.IStore) */ @Override public void setStore(IStore store) { this.store = store; } /* (non-Javadoc) * @see org.eclipsetrader.core.repositories.IStoreObject#setStoreProperties(org.eclipsetrader.core.repositories.IStoreProperties) */ @Override public void setStoreProperties(IStoreProperties storeProperties) { this.storeProperties = storeProperties; } } protected HistoryDay() { } public HistoryDay(ISecurity security, TimeSpan timeSpan) { this.security = security; this.timeSpan = timeSpan; } public HistoryDay(ISecurity security, TimeSpan timeSpan, IStore[] store, IStoreProperties[] storeProperties) { this.security = security; this.timeSpan = timeSpan; setStoreProperties(store, storeProperties); } public HistoryDay(ISecurity security, TimeSpan timeSpan, IOHLC[] bars) { this.security = security; this.timeSpan = timeSpan; this.bars = bars; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getAdjustedOHLC() */ @Override public IOHLC[] getAdjustedOHLC() { return bars; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getFirst() */ @Override public IOHLC getFirst() { return bars != null && bars.length != 0 ? bars[0] : null; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getHighest() */ @Override public IOHLC getHighest() { return highest; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getLast() */ @Override public IOHLC getLast() { return bars != null && bars.length != 0 ? bars[bars.length - 1] : null; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getLowest() */ @Override public IOHLC getLowest() { return lowest; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getOHLC() */ @Override public IOHLC[] getOHLC() { return bars; } public void setOHLC(IOHLC[] bars) { if (Arrays.equals(this.bars, bars)) { return; } IOHLC[] oldBars = this.bars; List<IOHLC> l = new ArrayList<IOHLC>(Arrays.asList(bars)); Collections.sort(l, new Comparator<IOHLC>() { @Override public int compare(IOHLC o1, IOHLC o2) { return o1.getDate().compareTo(o2.getDate()); } }); this.bars = l.toArray(new IOHLC[l.size()]); updateStoreObjects(); updateRange(); propertyChangeSupport.firePropertyChange(IPropertyConstants.BARS, oldBars, this.bars); } protected IStoreObject[] updateStoreObjects() { Set<StoreObject> updatedStoreObjects = new HashSet<StoreObject>(); IRepository repository = null; if (storeObjects.size() != 0) { IStore store = storeObjects.values().iterator().next().getStore(); if (store != null) { repository = store.getRepository(); } } if (bars.length != 0) { Calendar c = Calendar.getInstance(); int dayOfYear = -1; Date date = null; List<IOHLC> list = new ArrayList<IOHLC>(2048); for (IOHLC d : bars) { c.setTime(d.getDate()); c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.MINUTE, 0); c.set(Calendar.SECOND, 0); c.set(Calendar.MILLISECOND, 0); if (c.get(Calendar.DAY_OF_YEAR) != dayOfYear) { if (list.size() != 0 && date != null) { StoreObject object = storeObjects.get(date); if (object == null) { IStoreProperties properties = new StoreProperties(); properties.setProperty(IPropertyConstants.OBJECT_TYPE, IHistory.class.getName()); properties.setProperty(IPropertyConstants.SECURITY, security); properties.setProperty(IPropertyConstants.BARS_DATE, date); properties.setProperty(timeSpan.toString(), list.toArray(new IOHLC[list.size()])); object = new StoreObject(repository != null ? repository.createObject() : null, properties); storeObjects.put(date, object); updatedStoreObjects.add(object); } else { IStoreProperties properties = object.getStoreProperties(); properties.setProperty(timeSpan.toString(), list.toArray(new IOHLC[list.size()])); object.setStoreProperties(properties); updatedStoreObjects.add(object); } list = new ArrayList<IOHLC>(2048); } dayOfYear = c.get(Calendar.DAY_OF_YEAR); } list.add(d); date = c.getTime(); } if (list.size() != 0 && date != null) { StoreObject object = storeObjects.get(date); if (object == null) { IStoreProperties properties = new StoreProperties(); properties.setProperty(IPropertyConstants.OBJECT_TYPE, IHistory.class.getName()); properties.setProperty(IPropertyConstants.SECURITY, security); properties.setProperty(IPropertyConstants.BARS_DATE, date); properties.setProperty(timeSpan.toString(), list.toArray(new IOHLC[list.size()])); object = new StoreObject(repository != null ? repository.createObject() : null, properties); storeObjects.put(date, object); updatedStoreObjects.add(object); } else { IStoreProperties properties = object.getStoreProperties(); properties.setProperty(timeSpan.toString(), list.toArray(new IOHLC[list.size()])); object.setStoreProperties(properties); updatedStoreObjects.add(object); } } } return updatedStoreObjects.toArray(new IStoreObject[updatedStoreObjects.size()]); } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getSecurity() */ @Override public ISecurity getSecurity() { return security; } protected void setSecurity(ISecurity security) { this.security = security; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getSplits() */ @Override public ISplit[] getSplits() { return null; } public void setSplits(ISplit[] splits) { // Do nothing } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getSubset(java.util.Date, java.util.Date) */ @Override public IHistory getSubset(Date first, Date last) { List<IOHLC> l = new ArrayList<IOHLC>(); for (IOHLC b : bars) { if ((first == null || !b.getDate().before(first)) && (last == null || !b.getDate().after(last))) { l.add(b); } } return new HistoryDay(security, timeSpan, l.toArray(new IOHLC[l.size()])); } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getSubset(java.util.Date, java.util.Date, org.eclipsetrader.core.feed.TimeSpan) */ @Override public IHistory getSubset(Date first, Date last, TimeSpan aggregation) { if (this.timeSpan != null && this.timeSpan.equals(aggregation)) { return getSubset(first, last); } return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getDay(java.util.Date) */ @Override public IHistory[] getDay(Date date) { return null; } /* (non-Javadoc) * @see org.eclipsetrader.core.feed.IHistory#getTimeSpan() */ @Override public TimeSpan getTimeSpan() { return timeSpan; } /* (non-Javadoc) * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class) */ @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public Object getAdapter(Class adapter) { if (adapter.isAssignableFrom(getClass())) { return this; } if (adapter.isAssignableFrom(PropertyChangeSupport.class)) { return propertyChangeSupport; } if (adapter.isAssignableFrom(IStoreObject[].class)) { Collection<StoreObject> c = storeObjects.values(); return c.toArray(new IStoreObject[c.size()]); } return null; } public void setStoreProperties(IStore[] store, IStoreProperties[] storeProperties) { Assert.isTrue(store.length == storeProperties.length, "IStore and IStoreProperties arrays must be of same size!"); storeObjects.clear(); if (storeProperties.length != 0) { this.security = (ISecurity) storeProperties[0].getProperty(IPropertyConstants.SECURITY); } List<IOHLC> l1 = new ArrayList<IOHLC>(2048); for (int i = 0; i < store.length; i++) { StoreObject object = new StoreObject(store[i], storeProperties[i]); Date date = (Date) storeProperties[i].getProperty(IPropertyConstants.BARS_DATE); storeObjects.put(date, object); IOHLC[] bars = (IOHLC[]) storeProperties[i].getProperty(timeSpan.toString()); if (bars == null) { IOHLC[] minuteBars = getLowestTimespanBars(storeProperties[i]); if (minuteBars != null) { Date startDate = null, endDate = null; Double open = null, high = null, low = null, close = null; Long volume = 0L; Calendar c = Calendar.getInstance(); List<IOHLC> l = new ArrayList<IOHLC>(); for (IOHLC currentBar : minuteBars) { if (startDate != null && !currentBar.getDate().before(endDate)) { l.add(new OHLC(startDate, open, high, low, close, volume)); startDate = null; } if (startDate == null) { c.setTime(currentBar.getDate()); startDate = c.getTime(); c.add(Calendar.MINUTE, timeSpan.getLength()); endDate = c.getTime(); open = high = low = close = null; volume = 0L; } if (open == null) { open = currentBar.getOpen(); } high = high != null ? Math.max(high, currentBar.getHigh()) : currentBar.getHigh(); low = low != null ? Math.min(low, currentBar.getLow()) : currentBar.getLow(); close = currentBar.getClose(); volume += currentBar.getVolume(); } if (startDate != null) { l.add(new OHLC(startDate, open, high, low, close, volume)); } bars = l.toArray(new IOHLC[l.size()]); } } if (bars != null) { l1.addAll(Arrays.asList(bars)); } } Collections.sort(l1, new Comparator<IOHLC>() { @Override public int compare(IOHLC o1, IOHLC o2) { return o1.getDate().compareTo(o2.getDate()); } }); IOHLC[] newBars = l1.toArray(new IOHLC[l1.size()]); if (Arrays.equals(this.bars, newBars)) { return; } IOHLC[] oldBars = this.bars; this.bars = newBars; updateRange(); propertyChangeSupport.firePropertyChange(IPropertyConstants.BARS, oldBars, this.bars); } IOHLC[] getLowestTimespanBars(IStoreProperties storeProperties) { TimeSpan lowestTimeSpan = null; for (String name : storeProperties.getPropertyNames()) { TimeSpan propertyTimeSpan = TimeSpan.fromString(name); if (propertyTimeSpan != null && propertyTimeSpan.lowerThan(timeSpan)) { if (lowestTimeSpan == null || propertyTimeSpan.lowerThan(lowestTimeSpan)) { lowestTimeSpan = propertyTimeSpan; } } } if (lowestTimeSpan != null) { return (IOHLC[]) storeProperties.getProperty(lowestTimeSpan.toString()); } return null; } protected void updateRange() { highest = null; lowest = null; for (IOHLC b : bars) { if (highest == null || b.getHigh() > highest.getHigh()) { highest = b; } if (lowest == null || b.getLow() < lowest.getLow()) { lowest = b; } } } }