/* * 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.Collections.Generic; using System.Linq; using NUnit.Framework; using Python.Runtime; using QuantConnect.Brokerages; using QuantConnect.Algorithm; using QuantConnect.Data.Market; using QuantConnect.Orders; using QuantConnect.Tests.Engine.DataFeeds; using QuantConnect.Securities; using QuantConnect.Benchmarks; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Interfaces; using QuantConnect.Orders.Slippage; namespace QuantConnect.Tests.Algorithm { /// /// Test class for /// - SetBrokerageModel() in QCAlgorithm /// - Default market for new securities /// [TestFixture] public class AlgorithmSetBrokerageTests { private QCAlgorithm _algo; private const string ForexSym = "EURUSD"; private const string Sym = "SPY"; /// /// Instatiate a new algorithm before each test. /// Clear the so that no symbols and associated brokerage models are cached between test /// [SetUp] public void Setup() { _algo = new QCAlgorithm(); _algo.SubscriptionManager.SetDataManager(new DataManagerStub(_algo)); SymbolCache.TryRemove(ForexSym); SymbolCache.TryRemove(Sym); } /// /// The default market for FOREX should be Oanda /// [Test] public void DefaultBrokerageModel_IsOanda_ForForex() { var forex = _algo.AddForex(ForexSym); Assert.IsTrue(forex.Symbol.ID.Market == Market.Oanda); Assert.IsTrue(_algo.BrokerageModel.GetType() == typeof(DefaultBrokerageModel)); } [Test] public void PythonCallPureCSharpSetBrokerageModel() { using (Py.GIL()) { var model = new AlphaStreamsBrokerageModel().ToPython(); _algo.SetBrokerageModel(model); Assert.DoesNotThrow(() => _algo.BrokerageModel.ApplySplit(new List(), new Split())); } } [Test] public void PythonCallSetBrokerageModel() { using (Py.GIL()) { var model = PyModule.FromString("testModule", @" from AlgorithmImports import * class Test(AlphaStreamsBrokerageModel): def GetLeverage(self, security): return 12").GetAttr("Test"); _algo.SetBrokerageModel(model.Invoke()); var equity = _algo.AddEquity(Sym); Assert.DoesNotThrow(() => _algo.BrokerageModel.ApplySplit(new List(), new Split())); Assert.AreEqual(12m, _algo.BrokerageModel.GetLeverage(equity)); } } /// /// The default market for equities should be USA /// [Test] public void DefaultBrokerageModel_IsUSA_ForEquity() { var equity = _algo.AddEquity(Sym); Assert.IsTrue(equity.Symbol.ID.Market == Market.USA); Assert.IsTrue(_algo.BrokerageModel.GetType() == typeof(DefaultBrokerageModel)); } /// /// The default market for options should be USA /// [Test] public void DefaultBrokerageModel_IsUSA_ForOption() { var option = _algo.AddOption(Sym); Assert.IsTrue(option.Symbol.ID.Market == Market.USA); Assert.IsTrue(_algo.BrokerageModel.GetType() == typeof(DefaultBrokerageModel)); } /// /// Brokerage model for an algorithm can be changed using /// This changes the brokerage models used when forex currency pairs are added via AddForex and no brokerage is specified. /// [Test] public void BrokerageModel_CanBeSpecifiedWith_SetBrokerageModel() { _algo.SetBrokerageModel(BrokerageName.OandaBrokerage); var forex = _algo.AddForex(ForexSym); string brokerage = GetDefaultBrokerageForSecurityType(SecurityType.Forex); Assert.IsTrue(forex.Symbol.ID.Market == Market.Oanda); Assert.IsTrue(_algo.BrokerageModel.GetType() == typeof(OandaBrokerageModel)); Assert.IsTrue(brokerage == Market.Oanda); } /// /// Specifying the market in will change the market of the security created. /// [Test] public void BrokerageModel_CanBeSpecifiedWith_AddForex() { var forex = _algo.AddForex(ForexSym, Resolution.Minute, Market.FXCM); string brokerage = GetDefaultBrokerageForSecurityType(SecurityType.Forex); Assert.IsTrue(forex.Symbol.ID.Market == Market.FXCM); Assert.IsTrue(_algo.BrokerageModel.GetType() == typeof(DefaultBrokerageModel)); Assert.IsTrue(brokerage == Market.Oanda); // Doesn't change brokerage defined in BrokerageModel.DefaultMarkets } /// /// The method should use the default brokerage for the sepcific security. /// Setting the brokerage with will affect the market of securities added with /// [Test] public void AddSecurity_Follows_SetBrokerageModel() { // No brokerage set var equity = _algo.AddSecurity(SecurityType.Equity, Sym); string equityBrokerage = GetDefaultBrokerageForSecurityType(SecurityType.Equity); Assert.IsTrue(equity.Symbol.ID.Market == Market.USA); Assert.IsTrue(_algo.BrokerageModel.GetType() == typeof(DefaultBrokerageModel)); Assert.IsTrue(equityBrokerage == Market.USA); // Set Brokerage _algo.SetBrokerageModel(BrokerageName.OandaBrokerage); var sec = _algo.AddSecurity(SecurityType.Forex, ForexSym, Resolution.Daily, false, 1, false); string forexBrokerage = GetDefaultBrokerageForSecurityType(SecurityType.Forex); Assert.IsTrue(sec.Symbol.ID.Market == Market.Oanda); Assert.IsTrue(_algo.BrokerageModel.GetType() == typeof(OandaBrokerageModel)); Assert.IsTrue(forexBrokerage == Market.Oanda); } [Test] public void AddSecurityCanAddWithSameTickerAndDifferentMarket() { var fxcmSecurity = _algo.AddSecurity(SecurityType.Forex, "EURUSD", Resolution.Minute, Market.FXCM, true, 1m, true); var oandaSecurity = _algo.AddSecurity(SecurityType.Forex, "EURUSD", Resolution.Minute, Market.Oanda, true, 1m, true); Assert.AreEqual(2, _algo.Securities.Count); Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.FXCM)); Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.Oanda)); Assert.AreEqual(Market.FXCM, fxcmSecurity.Symbol.ID.Market); Assert.AreEqual(Market.Oanda, oandaSecurity.Symbol.ID.Market); } [Test] public void AddForexCanAddWithSameTickerAndDifferentMarket() { var fxcmSecurity = _algo.AddForex("EURUSD", Resolution.Minute, Market.FXCM); var oandaSecurity = _algo.AddForex("EURUSD", Resolution.Minute, Market.Oanda); Assert.AreEqual(2, _algo.Securities.Count); Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.FXCM)); Assert.IsNotNull(_algo.Securities.Single(pair => pair.Key.ID.Market == Market.Oanda)); Assert.AreEqual(Market.FXCM, fxcmSecurity.Symbol.ID.Market); Assert.AreEqual(Market.Oanda, oandaSecurity.Symbol.ID.Market); } [TestCase(Language.CSharp)] [TestCase(Language.Python)] public void BrokerageNameFollowsSetBrokerageModel(Language language) { if (language == Language.CSharp) { Assert.AreEqual(BrokerageName.Default, _algo.BrokerageName); _algo.SetBrokerageModel(BrokerageName.OandaBrokerage); Assert.AreEqual(BrokerageName.OandaBrokerage, _algo.BrokerageName); _algo.SetBrokerageModel(new InteractiveBrokersBrokerageModel()); Assert.AreEqual(BrokerageName.InteractiveBrokersBrokerage, _algo.BrokerageName); _algo.SetBrokerageModel(new CustomBrokerageModel()); Assert.AreEqual(BrokerageName.Default, _algo.BrokerageName); } else { using (Py.GIL()) { var testModule = PyModule.FromString("testModule", @" from AlgorithmImports import * def getAlgorithm(): return QCAlgorithm() def setBrokerageModel(algorithm, brokerageModel): algorithm.SetBrokerageModel(brokerageModel) def getBrokerageName(algorithm): return algorithm.BrokerageName "); var getAlgorithm = testModule.GetAttr("getAlgorithm"); var algorithm = getAlgorithm.Invoke(); var setBrokerageModel = testModule.GetAttr("setBrokerageModel"); var getBrokerageName = testModule.GetAttr("getBrokerageName"); Assert.AreEqual(BrokerageName.Default, getBrokerageName.Invoke(algorithm).AsManagedObject(typeof(BrokerageName))); setBrokerageModel.Invoke(algorithm, BrokerageName.OandaBrokerage.ToPython()); Assert.AreEqual(BrokerageName.OandaBrokerage, getBrokerageName.Invoke(algorithm).AsManagedObject(typeof(BrokerageName))); setBrokerageModel.Invoke(algorithm, new InteractiveBrokersBrokerageModel().ToPython()); Assert.AreEqual(BrokerageName.InteractiveBrokersBrokerage, getBrokerageName.Invoke(algorithm).AsManagedObject(typeof(BrokerageName))); setBrokerageModel.Invoke(algorithm, new CustomBrokerageModel().ToPython()); Assert.AreEqual(BrokerageName.Default, getBrokerageName.Invoke(algorithm).AsManagedObject(typeof(BrokerageName))); } } } /// /// Returns the default market for a security type /// /// The type of security /// A string representing the default market of a security private string GetDefaultBrokerageForSecurityType(SecurityType secType) { string brokerage; _algo.BrokerageModel.DefaultMarkets.TryGetValue(secType, out brokerage); return brokerage; } private class CustomBrokerageModel : IBrokerageModel { public AccountType AccountType => throw new System.NotImplementedException(); public decimal RequiredFreeBuyingPowerPercent => throw new System.NotImplementedException(); public IReadOnlyDictionary DefaultMarkets => throw new System.NotImplementedException(); public void ApplySplit(List tickets, Split split) { throw new System.NotImplementedException(); } public bool CanExecuteOrder(Security security, Order order) { throw new System.NotImplementedException(); } public bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message) { throw new System.NotImplementedException(); } public bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message) { throw new System.NotImplementedException(); } public IBenchmark GetBenchmark(SecurityManager securities) { throw new System.NotImplementedException(); } public IBuyingPowerModel GetBuyingPowerModel(Security security) { throw new System.NotImplementedException(); } public IBuyingPowerModel GetBuyingPowerModel(Security security, AccountType accountType) { throw new System.NotImplementedException(); } public IFeeModel GetFeeModel(Security security) { throw new System.NotImplementedException(); } public IFillModel GetFillModel(Security security) { throw new System.NotImplementedException(); } public decimal GetLeverage(Security security) { throw new System.NotImplementedException(); } public IMarginInterestRateModel GetMarginInterestRateModel(Security security) { throw new System.NotImplementedException(); } public ISettlementModel GetSettlementModel(Security security) { throw new System.NotImplementedException(); } public ISettlementModel GetSettlementModel(Security security, AccountType accountType) { throw new System.NotImplementedException(); } public IShortableProvider GetShortableProvider(Security security) { throw new System.NotImplementedException(); } public ISlippageModel GetSlippageModel(Security security) { throw new System.NotImplementedException(); } } } }