/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using QuantConnect.Interfaces;
using QuantConnect.Util;
namespace QuantConnect.Data.Auxiliary
{
///
/// Provides a means of mapping a symbol at a point in time to the map file
/// containing that share class's mapping information
///
public class MapFileResolver : IEnumerable
{
private readonly Dictionary _mapFilesByPermtick;
private readonly Dictionary> _bySymbol;
///
/// Gets an empty , that is an instance that contains
/// zero mappings
///
public static readonly MapFileResolver Empty = new MapFileResolver(Enumerable.Empty());
///
/// Initializes a new instance of the by reading
/// in all files in the specified directory.
///
/// The data used to initialize this resolver.
public MapFileResolver(IEnumerable mapFiles)
{
_mapFilesByPermtick = new Dictionary(StringComparer.InvariantCultureIgnoreCase);
_bySymbol = new Dictionary>(StringComparer.InvariantCultureIgnoreCase);
foreach (var mapFile in mapFiles)
{
// add to our by path map
_mapFilesByPermtick.Add(mapFile.Permtick, mapFile);
foreach (var row in mapFile)
{
SortedList entries;
var mapFileRowEntry = new MapFileRowEntry(mapFile.Permtick, row);
if (!_bySymbol.TryGetValue(row.MappedSymbol, out entries))
{
entries = new SortedList();
_bySymbol[row.MappedSymbol] = entries;
}
if (entries.ContainsKey(mapFileRowEntry.MapFileRow.Date))
{
// check to verify it' the same data
if (!entries[mapFileRowEntry.MapFileRow.Date].Equals(mapFileRowEntry))
{
throw new DuplicateNameException("Attempted to assign different history for symbol.");
}
}
else
{
entries.Add(mapFileRowEntry.MapFileRow.Date, mapFileRowEntry);
}
}
}
}
///
/// Gets the map file matching the specified permtick
///
/// The permtick to match on
/// The map file matching the permtick, or null if not found
public MapFile GetByPermtick(string permtick)
{
MapFile mapFile;
_mapFilesByPermtick.TryGetValue(permtick.LazyToUpper(), out mapFile);
return mapFile;
}
///
/// Resolves the map file path containing the mapping information for the symbol defined at
///
/// The symbol as of to be mapped
/// The date associated with the
/// The map file responsible for mapping the symbol, if no map file is found, null is returned
public MapFile ResolveMapFile(string symbol, DateTime date)
{
// lookup the symbol's history
SortedList entries;
if (_bySymbol.TryGetValue(symbol, out entries))
{
if (entries.Count == 0)
{
return new MapFile(symbol, Enumerable.Empty());
}
// Return value of BinarySearch (from MSDN):
// The zero-based index of item in the sorted List, if item is found;
// otherwise, a negative number that is the bitwise complement of the index of the next element that is larger than item
// or, if there is no larger element, the bitwise complement of Count.
var indexOf = entries.Keys.BinarySearch(date);
if (indexOf >= 0)
{
symbol = entries.Values[indexOf].EntitySymbol;
}
else
{
if (indexOf == ~entries.Keys.Count)
{
// the searched date is greater than the last date in the list, return the last entry
indexOf = entries.Keys.Count - 1;
}
else
{
// if negative, it's the bitwise complement of where it should be
indexOf = ~indexOf;
}
symbol = entries.Values[indexOf].EntitySymbol;
}
}
// secondary search for exact mapping, find path than ends with symbol.csv
MapFile mapFile;
if (!_mapFilesByPermtick.TryGetValue(symbol, out mapFile)
|| mapFile.FirstDate > date && date != SecurityIdentifier.DefaultDate)
{
return new MapFile(symbol, Enumerable.Empty());
}
return mapFile;
}
///
/// Combines the map file row with the map file path that produced the row
///
class MapFileRowEntry : IEquatable
{
///
/// Gets the map file row
///
public MapFileRow MapFileRow { get; private set; }
///
/// Gets the full path to the map file that produced this row
///
public string EntitySymbol { get; private set; }
///
/// Initializes a new instance of the class
///
/// The map file that produced this row
/// The map file row data
public MapFileRowEntry(string entitySymbol, MapFileRow mapFileRow)
{
MapFileRow = mapFileRow;
EntitySymbol = entitySymbol;
}
///
/// Indicates whether the current object is equal to another object of the same type.
///
///
/// true if the current object is equal to the parameter; otherwise, false.
///
/// An object to compare with this object.
public bool Equals(MapFileRowEntry other)
{
if (other == null) return false;
return other.MapFileRow.Date == MapFileRow.Date
&& other.MapFileRow.MappedSymbol == MapFileRow.MappedSymbol;
}
///
/// Returns a string that represents the current object.
///
///
/// A string that represents the current object.
///
/// 2
public override string ToString()
{
return MapFileRow.Date + ": " + MapFileRow.MappedSymbol + ": " + EntitySymbol;
}
}
#region Implementation of IEnumerable
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through the collection.
///
/// 1
public IEnumerator GetEnumerator()
{
return _mapFilesByPermtick.Values.GetEnumerator();
}
///
/// Returns an enumerator that iterates through a collection.
///
///
/// An object that can be used to iterate through the collection.
///
/// 2
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
}