/*
* 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 NodaTime;
using System.Linq;
using QuantConnect.Util;
using System.Collections.Generic;
namespace QuantConnect.Securities
{
///
/// Base exchange class providing information and helper tools for reading the current exchange situation
///
public class SecurityExchange
{
private LocalTimeKeeper _timeProvider;
///
/// Gets the for this exchange
///
public SecurityExchangeHours Hours { get; private set; }
///
/// Gets the time zone for this exchange
///
public DateTimeZone TimeZone => Hours.TimeZone;
///
/// Number of trading days per year for this security. By default the market is open 365 days per year.
///
/// Used for performance statistics to calculate sharpe ratio accurately
public virtual int TradingDaysPerYear => 365;
///
/// Time from the most recent data
///
public DateTime LocalTime => _timeProvider.LocalTime;
///
/// Boolean property for quickly testing if the exchange is open.
///
public bool ExchangeOpen => Hours.IsOpen(LocalTime, false);
///
/// Boolean property for quickly testing if the exchange is 10 minutes away from closing.
///
public bool ClosingSoon => IsClosingSoon(minutesToClose:10);
///
/// Initializes a new instance of the class using the specified
/// exchange hours to determine open/close times
///
/// Contains the weekly exchange schedule plus holidays
public SecurityExchange(SecurityExchangeHours exchangeHours)
{
Hours = exchangeHours;
}
///
/// Set the current datetime in terms of the exchange's local time zone
///
/// Most recent data tick
public void SetLocalDateTimeFrontierProvider(LocalTimeKeeper timeProvider)
{
_timeProvider = timeProvider;
}
///
/// Check if the *date* is open.
///
/// This is useful for first checking the date list, and then the market hours to save CPU cycles
/// Date to check
/// True to consider days with extended market hours only as open
/// Return true if the exchange is open for this date
public bool DateIsOpen(DateTime dateToCheck, bool extendedMarketHours = false)
{
return Hours.IsDateOpen(dateToCheck, extendedMarketHours);
}
///
/// Check if this DateTime is open.
///
/// DateTime to check
/// Boolean true if the market is open
public bool DateTimeIsOpen(DateTime dateTime)
{
return Hours.IsOpen(dateTime, false);
}
///
/// Determines if the exchange was open at any time between start and stop
///
public bool IsOpenDuringBar(DateTime barStartTime, DateTime barEndTime, bool isExtendedMarketHours)
{
return Hours.IsOpen(barStartTime, barEndTime, isExtendedMarketHours);
}
///
/// Determines if the exchange is going to close in the next provided minutes
///
/// Minutes to close to check
/// Returns true if the exchange is going to close in the next provided minutes
public bool IsClosingSoon(int minutesToClose)
{
return !Hours.IsOpen(LocalTime.AddMinutes(minutesToClose), false);
}
///
/// Sets the regular market hours for the specified days If no days are specified then
/// all days will be updated.
///
/// Specifies each segment of the market hours, such as premarket/market/postmark
/// The days of the week to set these times for
public void SetMarketHours(IEnumerable marketHoursSegments, params DayOfWeek[] days)
{
if (days.IsNullOrEmpty()) days = Enum.GetValues(typeof(DayOfWeek)).OfType().ToArray();
var marketHours = Hours.MarketHours.ToDictionary();
marketHoursSegments = marketHoursSegments as IList ?? marketHoursSegments.ToList();
foreach (var day in days)
{
marketHours[day] = new LocalMarketHours(day, marketHoursSegments);
}
// create a new exchange hours instance for the new hours
Hours = new SecurityExchangeHours(Hours.TimeZone, Hours.Holidays, marketHours, Hours.EarlyCloses, Hours.LateOpens);
}
}
}