/*
* 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 QuantConnect.Util;
using QuantConnect.Logging;
using System.Threading.Tasks;
using QuantConnect.Interfaces;
using System.Collections.Generic;
namespace QuantConnect.Data.Auxiliary
{
///
/// Provides an implementation of that reads from a local zip file
///
public class LocalZipMapFileProvider : IMapFileProvider
{
private Dictionary _cache;
private IDataProvider _dataProvider;
private object _lock;
///
/// The cached refresh period for the map files
///
/// Exposed for testing
protected virtual TimeSpan CacheRefreshPeriod
{
get
{
var dueTime = Time.GetNextLiveAuxiliaryDataDueTime();
if (dueTime > TimeSpan.FromMinutes(10))
{
// Clear the cache before the auxiliary due time to avoid race conditions with consumers
return dueTime - TimeSpan.FromMinutes(10);
}
return dueTime;
}
}
///
/// Creates a new instance of the
///
public LocalZipMapFileProvider()
{
_lock = new object();
_cache = new Dictionary();
}
///
/// Initializes our MapFileProvider by supplying our dataProvider
///
/// DataProvider to use
public void Initialize(IDataProvider dataProvider)
{
if (_dataProvider != null)
{
return;
}
_dataProvider = dataProvider;
StartExpirationTask();
}
///
/// Gets a representing all the map files for the specified market
///
/// Key used to fetch a map file resolver. Specifying market and security type
/// A containing all map files for the specified market
public MapFileResolver Get(AuxiliaryDataKey auxiliaryDataKey)
{
MapFileResolver result;
// we use a lock so that only 1 thread loads the map file resolver while the rest wait
// else we could have multiple threads loading the map file resolver at the same time!
lock (_lock)
{
if (!_cache.TryGetValue(auxiliaryDataKey, out result))
{
_cache[auxiliaryDataKey] = result = GetMapFileResolver(auxiliaryDataKey);
}
}
return result;
}
///
/// Helper method that will clear any cached factor files in a daily basis, this is useful for live trading
///
protected virtual void StartExpirationTask()
{
lock (_lock)
{
// we clear the seeded markets so they are reloaded
_cache = new Dictionary();
}
_ = Task.Delay(CacheRefreshPeriod).ContinueWith(_ => StartExpirationTask());
}
private MapFileResolver GetMapFileResolver(AuxiliaryDataKey auxiliaryDataKey)
{
var market = auxiliaryDataKey.Market;
var timestamp = DateTime.UtcNow.ConvertFromUtc(TimeZones.NewYork);
var todayNewYork = timestamp.Date;
var yesterdayNewYork = todayNewYork.AddDays(-1);
// start the search with yesterday, today's file will be available tomorrow
var count = 0;
var date = yesterdayNewYork;
do
{
var zipFileName = MapFileZipHelper.GetMapFileZipFileName(market, date, auxiliaryDataKey.SecurityType);
// Fetch a stream for our zip from our data provider
var stream = _dataProvider.Fetch(zipFileName);
// If we found a file we can read it
if (stream != null)
{
Log.Trace("LocalZipMapFileProvider.Get({0}): Fetched map files for: {1} NY", market, date.ToShortDateString());
var result = new MapFileResolver(MapFileZipHelper.ReadMapFileZip(stream, market, auxiliaryDataKey.SecurityType));
stream.DisposeSafely();
return result;
}
// prevent infinite recursion if something is wrong
if (count++ > 30)
{
throw new InvalidOperationException($"LocalZipMapFileProvider couldn't find any map files going all the way back to {date} for {market}");
}
date = date.AddDays(-1);
}
while (true);
}
}
}