diff --git a/Common/Securities/Future/Futures.cs b/Common/Securities/Future/Futures.cs index eab86c810230..b55af9a130dc 100644 --- a/Common/Securities/Future/Futures.cs +++ b/Common/Securities/Future/Futures.cs @@ -1543,6 +1543,12 @@ public static class Indices /// The symbol public const string BseSensex = "SENSEX"; + /// + /// Hang Seng Index + /// + /// The symbol + public const string HangSeng = "HSI"; + /// /// Micro E-mini S&P 500 Index Futures /// diff --git a/Common/Securities/Future/FuturesExpiryFunctions.cs b/Common/Securities/Future/FuturesExpiryFunctions.cs index e841fa5d1b6b..ed162b3faae9 100644 --- a/Common/Securities/Future/FuturesExpiryFunctions.cs +++ b/Common/Securities/Future/FuturesExpiryFunctions.cs @@ -909,6 +909,24 @@ public static Func FuturesExpiryFunction(Symbol symbol) }) }, + // HSI Index Futures:https://www.hkex.com.hk/Products/Listed-Derivatives/Equity-Index/Hang-Seng-Index-(HSI)/Hang-Seng-Index-Futures?sc_lang=en#&product=HSI + {Symbol.Create(Futures.Indices.HangSeng, SecurityType.Future, Market.HKFE), (time => + { + // Short-dated Futures: Spot, next calendar month & next two calendar quarter months; and Long-dated Futures: the following 5 December months + + // The Business Day immediately preceding the last Business Day of the Contract Month + var lastDay = new DateTime(time.Year, time.Month, DateTime.DaysInMonth(time.Year, time.Month)); + var priorBusinessDay = lastDay.AddDays(-1); + + var holidays = FuturesExpiryUtilityFunctions.GetHolidays(Market.HKFE, Futures.Indices.HangSeng); + while (holidays.Contains(priorBusinessDay) || !priorBusinessDay.IsCommonBusinessDay()) + { + priorBusinessDay = priorBusinessDay.AddDays(-1); + } + return priorBusinessDay.Add(new TimeSpan(16, 0, 0)); + }) + }, + // MSCI Europe Net Total Return (USD) Futures: https://www.theice.com/products/71512951/MSCI-Europe-NTR-Index-Future-USD & https://www.theice.com/publicdocs/futures_us/exchange_notices/ICE_Futures_US_2022_TRADING_HOLIDAY_CALENDAR_20211118.pdf {Symbol.Create(Futures.Indices.MSCIEuropeNTR, SecurityType.Future, Market.NYSELIFFE), (time => { diff --git a/Common/Securities/Index/IndexSymbol.cs b/Common/Securities/Index/IndexSymbol.cs index 0685212198b9..132e4f1ec506 100644 --- a/Common/Securities/Index/IndexSymbol.cs +++ b/Common/Securities/Index/IndexSymbol.cs @@ -29,7 +29,8 @@ public static class IndexSymbol { "VIX", Market.CBOE }, { "SPXW", Market.CBOE }, { "NQX", "NASDAQ" }, - { "VIXW", Market.CBOE } + { "VIXW", Market.CBOE }, + { "HSI", Market.HKFE } }; /// diff --git a/Data/market-hours/market-hours-database.json b/Data/market-hours/market-hours-database.json index 1c30bc61b740..50cbc43dbf05 100644 --- a/Data/market-hours/market-hours-database.json +++ b/Data/market-hours/market-hours-database.json @@ -122303,6 +122303,796 @@ "12/26/2024", "12/31/2024" ] + }, + "Index-hkfe-[*]": { + "dataTimeZone": "Asia/Hong_Kong", + "exchangeTimeZone": "Asia/Hong_Kong", + "sunday": [], + "monday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "tuesday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "wednesday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "thursday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "friday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "saturday": [], + "holidays": [ + "1/2/2017", + "1/30/2017", + "1/31/2017", + "3/30/2017", + "4/4/2017", + "4/14/2017", + "4/17/2017", + "5/1/2017", + "5/3/2017", + "5/30/2017", + "10/2/2017", + "10/5/2017", + "12/25/2017", + "12/26/2017", + "1/1/2018", + "2/16/2018", + "2/19/2018", + "3/30/2018", + "4/2/2018", + "4/5/2018", + "5/1/2018", + "5/22/2018", + "6/18/2018", + "7/2/2018", + "9/25/2018", + "10/1/2018", + "10/17/2018", + "12/25/2018", + "12/26/2018", + "1/1/2019", + "2/5/2019", + "2/6/2019", + "2/7/2019", + "4/5/2019", + "4/19/2019", + "4/22/2019", + "5/1/2019", + "5/13/2019", + "6/7/2019", + "7/1/2019", + "10/1/2019", + "10/7/2019", + "12/25/2019", + "12/26/2019", + "1/1/2020", + "1/27/2020", + "1/28/2020", + "4/10/2020", + "4/30/2020", + "5/1/2020", + "6/25/2020", + "7/1/2020", + "10/1/2020", + "10/2/2020", + "10/26/2020", + "12/25/2020", + "1/1/2021", + "2/12/2021", + "2/15/2021", + "4/2/2021", + "4/5/2021", + "5/3/2021", + "5/19/2021", + "9/21/2021", + "10/1/2021", + "10/14/2021", + "12/27/2021", + "12/25/2021" + ], + "earlyCloses": { + "1/24/2020": "12:00:00", + "12/24/2020": "12:00:00", + "12/31/2020": "12:00:00", + "2/11/2021": "12:00:00", + "12/24/2021": "12:00:00", + "12/31/2021": "12:00:00", + "1/31/2022": "12:00:00", + "12/31/2022": "12:00:00" + }, + "lateOpens": {} + }, + "Index-hkfe-HSI": { + "dataTimeZone": "Asia/Hong_Kong", + "exchangeTimeZone": "Asia/Hong_Kong", + "sunday": [], + "monday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "tuesday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "wednesday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "thursday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "friday": [ + { + "start": "09:30:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "13:00:00", + "end": "16:00:00", + "state": "market" + } + ], + "saturday": [], + "holidays": [ + "1/2/2017", + "1/30/2017", + "1/31/2017", + "3/30/2017", + "4/4/2017", + "4/14/2017", + "4/17/2017", + "5/1/2017", + "5/3/2017", + "5/30/2017", + "10/2/2017", + "10/5/2017", + "12/25/2017", + "12/26/2017", + "1/1/2018", + "2/16/2018", + "2/19/2018", + "3/30/2018", + "4/2/2018", + "4/5/2018", + "5/1/2018", + "5/22/2018", + "6/18/2018", + "7/2/2018", + "9/25/2018", + "10/1/2018", + "10/17/2018", + "12/25/2018", + "12/26/2018", + "1/1/2019", + "2/5/2019", + "2/6/2019", + "2/7/2019", + "4/5/2019", + "4/19/2019", + "4/22/2019", + "5/1/2019", + "5/13/2019", + "6/7/2019", + "7/1/2019", + "10/1/2019", + "10/7/2019", + "12/25/2019", + "12/26/2019", + "1/1/2020", + "1/27/2020", + "1/28/2020", + "4/10/2020", + "4/30/2020", + "5/1/2020", + "6/25/2020", + "7/1/2020", + "10/1/2020", + "10/2/2020", + "10/26/2020", + "12/25/2020", + "1/1/2021", + "2/12/2021", + "2/15/2021", + "4/2/2021", + "4/5/2021", + "5/3/2021", + "5/19/2021", + "9/21/2021", + "10/1/2021", + "10/14/2021", + "12/27/2021", + "12/25/2021" + ], + "earlyCloses": { + "1/24/2020": "12:00:00", + "12/24/2020": "12:00:00", + "12/31/2020": "12:00:00", + "2/11/2021": "12:00:00", + "12/24/2021": "12:00:00", + "12/31/2021": "12:00:00", + "1/31/2022": "12:00:00", + "12/31/2022": "12:00:00" + }, + "lateOpens": {} + }, + "Future-hkfe-[*]": { + "dataTimeZone": "Asia/Hong_Kong", + "exchangeTimeZone": "Asia/Hong_Kong", + "sunday": [], + "monday": [ + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "tuesday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "wednesday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "thursday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "friday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "saturday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + } + ], + "holidays": [ + "1/2/2017", + "1/30/2017", + "1/31/2017", + "3/30/2017", + "4/4/2017", + "4/14/2017", + "4/17/2017", + "5/1/2017", + "5/3/2017", + "5/30/2017", + "10/2/2017", + "10/5/2017", + "12/25/2017", + "12/26/2017", + "1/1/2018", + "2/16/2018", + "2/19/2018", + "3/30/2018", + "4/2/2018", + "4/5/2018", + "5/1/2018", + "5/22/2018", + "6/18/2018", + "7/2/2018", + "9/25/2018", + "10/1/2018", + "10/17/2018", + "12/25/2018", + "12/26/2018", + "1/1/2019", + "2/5/2019", + "2/6/2019", + "2/7/2019", + "4/5/2019", + "4/19/2019", + "4/22/2019", + "5/1/2019", + "5/13/2019", + "6/7/2019", + "7/1/2019", + "10/1/2019", + "10/7/2019", + "12/25/2019", + "12/26/2019", + "1/1/2020", + "1/27/2020", + "1/28/2020", + "4/10/2020", + "4/30/2020", + "5/1/2020", + "6/25/2020", + "7/1/2020", + "10/1/2020", + "10/2/2020", + "10/26/2020", + "12/25/2020", + "1/1/2021", + "2/12/2021", + "2/15/2021", + "4/2/2021", + "4/5/2021", + "5/3/2021", + "5/19/2021", + "9/21/2021", + "10/1/2021", + "10/14/2021", + "12/27/2021", + "12/25/2021" + ], + "earlyCloses": { + "1/24/2020": "12:00:00", + "12/24/2020": "12:00:00", + "12/31/2020": "12:00:00", + "2/11/2021": "12:00:00", + "12/24/2021": "12:00:00", + "12/31/2021": "12:00:00", + "1/31/2022": "12:00:00", + "12/31/2022": "12:00:00" + }, + "lateOpens": {} + }, + "Future-hkfe-HSI": { + "dataTimeZone": "Asia/Hong_Kong", + "exchangeTimeZone": "Asia/Hong_Kong", + "sunday": [], + "monday": [ + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "tuesday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "wednesday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "thursday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "friday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + }, + { + "start": "08:45:00", + "end": "09:15:00", + "state": "premarket" + }, + { + "start": "09:15:00", + "end": "12:00:00", + "state": "market" + }, + { + "start": "12:30:00", + "end": "13:00:00", + "state": "premarket" + }, + { + "start": "13:00:00", + "end": "16:30:00", + "state": "market" + }, + { + "start": "17:15:00", + "end": "1.00:00:00", + "state": "postmarket" + } + ], + "saturday": [ + { + "start": "00:00:00", + "end": "03:00:00", + "state": "premarket" + } + ], + "holidays": [ + "1/2/2017", + "1/30/2017", + "1/31/2017", + "3/30/2017", + "4/4/2017", + "4/14/2017", + "4/17/2017", + "5/1/2017", + "5/3/2017", + "5/30/2017", + "10/2/2017", + "10/5/2017", + "12/25/2017", + "12/26/2017", + "1/1/2018", + "2/16/2018", + "2/19/2018", + "3/30/2018", + "4/2/2018", + "4/5/2018", + "5/1/2018", + "5/22/2018", + "6/18/2018", + "7/2/2018", + "9/25/2018", + "10/1/2018", + "10/17/2018", + "12/25/2018", + "12/26/2018", + "1/1/2019", + "2/5/2019", + "2/6/2019", + "2/7/2019", + "4/5/2019", + "4/19/2019", + "4/22/2019", + "5/1/2019", + "5/13/2019", + "6/7/2019", + "7/1/2019", + "10/1/2019", + "10/7/2019", + "12/25/2019", + "12/26/2019", + "1/1/2020", + "1/27/2020", + "1/28/2020", + "4/10/2020", + "4/30/2020", + "5/1/2020", + "6/25/2020", + "7/1/2020", + "10/1/2020", + "10/2/2020", + "10/26/2020", + "12/25/2020", + "1/1/2021", + "2/12/2021", + "2/15/2021", + "4/2/2021", + "4/5/2021", + "5/3/2021", + "5/19/2021", + "9/21/2021", + "10/1/2021", + "10/14/2021", + "12/27/2021", + "12/25/2021" + ], + "earlyCloses": { + "1/24/2020": "12:00:00", + "12/24/2020": "12:00:00", + "12/31/2020": "12:00:00", + "2/11/2021": "12:00:00", + "12/24/2021": "12:00:00", + "12/31/2021": "12:00:00", + "1/31/2022": "12:00:00", + "12/31/2022": "12:00:00" + }, + "lateOpens": {} } } } \ No newline at end of file diff --git a/Data/symbol-properties/symbol-properties-database.csv b/Data/symbol-properties/symbol-properties-database.csv index a769dcfb4fa8..c7039e76c291 100644 --- a/Data/symbol-properties/symbol-properties-database.csv +++ b/Data/symbol-properties/symbol-properties-database.csv @@ -402,6 +402,9 @@ india,NIFTY,future,Nifty50 Index,INR,0.05,75,1 india,BANKNIFTY,future,BankNifty Index,INR,0.05,25,1.0 india,SENSEX,future,BSE S&P Sensex Index,INR,0.05,25,1.0 +hkfe,HSI,future,Hang Seng Index,HKD,50,1,1.0 +hkfe,[*],index,,HKD,1,0.01,1 + # Futures options -- will default to Futures contract specs in case no entry exists cbot,OZB,futureoption,U.S. Treasury Bond American Futures Options,USD,1000.0,0.015625,1.0 cbot,OZC,futureoption,Corn American Futures Options,USD,5000.0,0.00125,1.0,,,100 diff --git a/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs b/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs index 508bde6d796e..d6201a4ad4e6 100644 --- a/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs +++ b/Tests/Common/Orders/Fees/InteractiveBrokersFeeModelTests.cs @@ -128,6 +128,39 @@ public void CalculatesCFDFee(string quoteCurrency, decimal price, decimal expect Assert.AreEqual(expectedFee, fee.Value.Amount); } + [TestCase(false)] + [TestCase(true)] + public void HongKongFutureFee(bool canonical) + { + var symbol = Symbols.CreateFutureSymbol(Futures.Indices.HangSeng, SecurityIdentifier.DefaultDate); + if (!canonical) + { + symbol = Symbols.CreateFutureSymbol(Futures.Indices.HangSeng, + FuturesExpiryFunctions.FuturesExpiryFunction(symbol)(new DateTime(2021, 12, 1))); + } + var entry = MarketHoursDatabase.FromDataFolder().GetEntry(symbol.ID.Market, symbol, symbol.SecurityType); + var properties = SymbolPropertiesDatabase.FromDataFolder() + .GetSymbolProperties(symbol.ID.Market, symbol, symbol.SecurityType, null); + var security = new Future(symbol, entry.ExchangeHours, + new Cash(properties.QuoteCurrency, 0, 0), + properties, + ErrorCurrencyConverter.Instance, + RegisteredSecurityDataTypesProvider.Null, + new SecurityCache() + ); + security.SetMarketPrice(new Tick(new DateTime(2021, 12, 1), security.Symbol, 100, 100)); + + var fee = _feeModel.GetOrderFee( + new OrderFeeParameters( + security, + new MarketOrder(security.Symbol, 1000, new DateTime(2021, 12, 1)) + ) + ); + + Assert.AreEqual(Currencies.HKD, fee.Value.Currency); + Assert.AreEqual(1000 * 40m, fee.Value.Amount); + } + [TestCase(OrderType.ComboMarket, 0.01, 250)] [TestCase(OrderType.ComboLimit, 0.01, 250)] [TestCase(OrderType.ComboLegLimit, 0.01, 250)] diff --git a/Tests/Common/Securities/SymbolPropertiesDatabaseTests.cs b/Tests/Common/Securities/SymbolPropertiesDatabaseTests.cs index 5d54f6617589..d7b48fcc387a 100644 --- a/Tests/Common/Securities/SymbolPropertiesDatabaseTests.cs +++ b/Tests/Common/Securities/SymbolPropertiesDatabaseTests.cs @@ -206,6 +206,7 @@ public void CanQueryMarketAfterRefresh() [TestCase(Market.ICE, SecurityType.Future)] [TestCase(Market.NYMEX, SecurityType.Future)] [TestCase(Market.SGX, SecurityType.Future)] + [TestCase(Market.HKFE, SecurityType.Future)] public void GetSymbolPropertiesListIsNotEmpty(string market, SecurityType securityType) { var db = SymbolPropertiesDatabase.FromDataFolder();