/// <summary> /// Primary entry point to setup a new algorithm /// </summary> /// <param name="parameters">The parameters object to use</param> /// <returns>True on successfully setting up the algorithm state, or false on error.</returns> public bool Setup(SetupHandlerParameters parameters) { var algorithm = parameters.Algorithm; var brokerage = parameters.Brokerage; // verify we were given the correct job packet type var liveJob = parameters.AlgorithmNodePacket as LiveNodePacket; if (liveJob == null) { AddInitializationError("BrokerageSetupHandler requires a LiveNodePacket"); return(false); } algorithm.Name = liveJob.GetAlgorithmName(); // verify the brokerage was specified if (string.IsNullOrWhiteSpace(liveJob.Brokerage)) { AddInitializationError("A brokerage must be specified"); return(false); } // attach to the message event to relay brokerage specific initialization messages EventHandler <BrokerageMessageEvent> brokerageOnMessage = (sender, args) => { if (args.Type == BrokerageMessageType.Error) { AddInitializationError($"Brokerage Error Code: {args.Code} - {args.Message}"); } }; try { // let the world know what we're doing since logging in can take a minute parameters.ResultHandler.SendStatusUpdate(AlgorithmStatus.LoggingIn, "Logging into brokerage..."); brokerage.Message += brokerageOnMessage; Log.Trace("BrokerageSetupHandler.Setup(): Connecting to brokerage..."); try { // this can fail for various reasons, such as already being logged in somewhere else brokerage.Connect(); } catch (Exception err) { Log.Error(err); AddInitializationError( $"Error connecting to brokerage: {err.Message}. " + "This may be caused by incorrect login credentials or an unsupported account type.", err); return(false); } if (!brokerage.IsConnected) { // if we're reporting that we're not connected, bail AddInitializationError("Unable to connect to brokerage."); return(false); } var message = $"{brokerage.Name} account base currency: {brokerage.AccountBaseCurrency ?? algorithm.AccountCurrency}"; var accountCurrency = brokerage.AccountBaseCurrency; if (liveJob.BrokerageData.ContainsKey(MaxAllocationLimitConfig)) { accountCurrency = Currencies.USD; message += ". Allocation limited, will use 'USD' account currency"; } Log.Trace($"BrokerageSetupHandler.Setup(): {message}"); algorithm.Debug(message); if (accountCurrency != null && accountCurrency != algorithm.AccountCurrency) { algorithm.SetAccountCurrency(accountCurrency); } Log.Trace("BrokerageSetupHandler.Setup(): Initializing algorithm..."); parameters.ResultHandler.SendStatusUpdate(AlgorithmStatus.Initializing, "Initializing algorithm..."); //Execute the initialize code: var controls = liveJob.Controls; var isolator = new Isolator(); var initializeComplete = isolator.ExecuteWithTimeLimit(TimeSpan.FromSeconds(300), () => { try { //Set the default brokerage model before initialize algorithm.SetBrokerageModel(_factory.GetBrokerageModel(algorithm.Transactions)); //Margin calls are disabled by default in live mode algorithm.Portfolio.MarginCallModel = MarginCallModel.Null; //Set our parameters algorithm.SetParameters(liveJob.Parameters); algorithm.SetAvailableDataTypes(BaseSetupHandler.GetConfiguredDataFeeds()); //Algorithm is live, not backtesting: algorithm.SetLiveMode(true); //Initialize the algorithm's starting date algorithm.SetDateTime(DateTime.UtcNow); //Set the source impl for the event scheduling algorithm.Schedule.SetEventSchedule(parameters.RealTimeHandler); var optionChainProvider = Composer.Instance.GetPart <IOptionChainProvider>(); if (optionChainProvider == null) { optionChainProvider = new CachingOptionChainProvider(new LiveOptionChainProvider()); } // set the option chain provider algorithm.SetOptionChainProvider(optionChainProvider); var futureChainProvider = Composer.Instance.GetPart <IFutureChainProvider>(); if (futureChainProvider == null) { futureChainProvider = new CachingFutureChainProvider(new LiveFutureChainProvider()); } // set the future chain provider algorithm.SetFutureChainProvider(futureChainProvider); // set the object store algorithm.SetObjectStore(parameters.ObjectStore); // If we're going to receive market data from IB, // set the default subscription limit to 100, // algorithms can override this setting in the Initialize method if (brokerage is InteractiveBrokersBrokerage && liveJob.DataQueueHandler.EndsWith("InteractiveBrokersBrokerage")) { algorithm.Settings.DataSubscriptionLimit = 100; } //Initialise the algorithm, get the required data: algorithm.Initialize(); if (liveJob.Brokerage != "PaperBrokerage") { //Zero the CashBook - we'll populate directly from brokerage foreach (var kvp in algorithm.Portfolio.CashBook) { kvp.Value.SetAmount(0); } } } catch (Exception err) { AddInitializationError(err.ToString(), err); } }, controls.RamAllocation, sleepIntervalMillis: 100); // entire system is waiting on this, so be as fast as possible if (Errors.Count != 0) { // if we already got an error just exit right away return(false); } if (!initializeComplete) { AddInitializationError("Initialization timed out."); return(false); } if (!LoadCashBalance(brokerage, algorithm)) { return(false); } if (!LoadExistingHoldingsAndOrders(brokerage, algorithm, parameters)) { return(false); } //Finalize Initialization algorithm.PostInitialize(); BaseSetupHandler.SetupCurrencyConversions(algorithm, parameters.UniverseSelection); if (algorithm.Portfolio.TotalPortfolioValue == 0) { algorithm.Debug("Warning: No cash balances or holdings were found in the brokerage account."); } string maxCashLimitStr; if (liveJob.BrokerageData.TryGetValue(MaxAllocationLimitConfig, out maxCashLimitStr)) { var maxCashLimit = decimal.Parse(maxCashLimitStr, NumberStyles.Any, CultureInfo.InvariantCulture); // If allocation exceeded by more than $10,000; block deployment if (algorithm.Portfolio.TotalPortfolioValue > (maxCashLimit + 10000m)) { var exceptionMessage = $"TotalPortfolioValue '{algorithm.Portfolio.TotalPortfolioValue}' exceeds allocation limit '{maxCashLimit}'"; algorithm.Debug(exceptionMessage); throw new ArgumentException(exceptionMessage); } } //Set the starting portfolio value for the strategy to calculate performance: StartingPortfolioValue = algorithm.Portfolio.TotalPortfolioValue; StartingDate = DateTime.Now; // we set the free portfolio value based on the initial total value and the free percentage value algorithm.Settings.FreePortfolioValue = algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage; } catch (Exception err) { AddInitializationError(err.ToString(), err); } finally { if (brokerage != null) { brokerage.Message -= brokerageOnMessage; } } return(Errors.Count == 0); }
/// <summary> /// Primary entry point to setup a new algorithm /// </summary> /// <param name="parameters">The parameters object to use</param> /// <returns>True on successfully setting up the algorithm state, or false on error.</returns> public bool Setup(SetupHandlerParameters parameters) { var algorithm = parameters.Algorithm; var brokerage = parameters.Brokerage; // verify we were given the correct job packet type var liveJob = parameters.AlgorithmNodePacket as LiveNodePacket; if (liveJob == null) { AddInitializationError("BrokerageSetupHandler requires a LiveNodePacket"); return(false); } algorithm.Name = liveJob.GetAlgorithmName(); // verify the brokerage was specified if (string.IsNullOrWhiteSpace(liveJob.Brokerage)) { AddInitializationError("A brokerage must be specified"); return(false); } // attach to the message event to relay brokerage specific initialization messages EventHandler <BrokerageMessageEvent> brokerageOnMessage = (sender, args) => { if (args.Type == BrokerageMessageType.Error) { AddInitializationError($"Brokerage Error Code: {args.Code} - {args.Message}"); } }; try { // let the world know what we're doing since logging in can take a minute parameters.ResultHandler.SendStatusUpdate(AlgorithmStatus.LoggingIn, "Logging into brokerage..."); brokerage.Message += brokerageOnMessage; Log.Trace("BrokerageSetupHandler.Setup(): Connecting to brokerage..."); try { // this can fail for various reasons, such as already being logged in somewhere else brokerage.Connect(); } catch (Exception err) { Log.Error(err); AddInitializationError( $"Error connecting to brokerage: {err.Message}. " + "This may be caused by incorrect login credentials or an unsupported account type.", err); return(false); } if (!brokerage.IsConnected) { // if we're reporting that we're not connected, bail AddInitializationError("Unable to connect to brokerage."); return(false); } var message = $"{brokerage.Name} account base currency: {brokerage.AccountBaseCurrency ?? algorithm.AccountCurrency}"; Log.Trace($"BrokerageSetupHandler.Setup(): {message}"); algorithm.Debug(message); if (brokerage.AccountBaseCurrency != null && brokerage.AccountBaseCurrency != algorithm.AccountCurrency) { algorithm.SetAccountCurrency(brokerage.AccountBaseCurrency); } Log.Trace("BrokerageSetupHandler.Setup(): Initializing algorithm..."); parameters.ResultHandler.SendStatusUpdate(AlgorithmStatus.Initializing, "Initializing algorithm..."); //Execute the initialize code: var controls = liveJob.Controls; var isolator = new Isolator(); var initializeComplete = isolator.ExecuteWithTimeLimit(TimeSpan.FromSeconds(300), () => { try { //Set the default brokerage model before initialize algorithm.SetBrokerageModel(_factory.GetBrokerageModel(algorithm.Transactions)); //Margin calls are disabled by default in live mode algorithm.Portfolio.MarginCallModel = MarginCallModel.Null; //Set our parameters algorithm.SetParameters(liveJob.Parameters); algorithm.SetAvailableDataTypes(GetConfiguredDataFeeds()); //Algorithm is live, not backtesting: algorithm.SetLiveMode(true); //Initialize the algorithm's starting date algorithm.SetDateTime(DateTime.UtcNow); //Set the source impl for the event scheduling algorithm.Schedule.SetEventSchedule(parameters.RealTimeHandler); // set the option chain provider algorithm.SetOptionChainProvider(new CachingOptionChainProvider(new LiveOptionChainProvider())); // set the future chain provider algorithm.SetFutureChainProvider(new CachingFutureChainProvider(new LiveFutureChainProvider())); // set the object store algorithm.SetObjectStore(parameters.ObjectStore); // If we're going to receive market data from IB, // set the default subscription limit to 100, // algorithms can override this setting in the Initialize method if (brokerage is InteractiveBrokersBrokerage && liveJob.DataQueueHandler.EndsWith("InteractiveBrokersBrokerage")) { algorithm.Settings.DataSubscriptionLimit = 100; } //Initialise the algorithm, get the required data: algorithm.Initialize(); if (liveJob.Brokerage != "PaperBrokerage") { //Zero the CashBook - we'll populate directly from brokerage foreach (var kvp in algorithm.Portfolio.CashBook) { kvp.Value.SetAmount(0); } } } catch (Exception err) { AddInitializationError(err.ToString(), err); } }, controls.RamAllocation, sleepIntervalMillis: 50); // entire system is waiting on this, so be as fast as possible if (!initializeComplete) { AddInitializationError("Initialization timed out."); return(false); } Log.Trace("BrokerageSetupHandler.Setup(): Fetching cash balance from brokerage..."); try { // set the algorithm's cash balance for each currency var cashBalance = brokerage.GetCashBalance(); foreach (var cash in cashBalance) { Log.Trace("BrokerageSetupHandler.Setup(): Setting " + cash.Currency + " cash to " + cash.Amount); algorithm.Portfolio.SetCash(cash.Currency, cash.Amount, 0); } } catch (Exception err) { Log.Error(err); AddInitializationError("Error getting cash balance from brokerage: " + err.Message, err); return(false); } var supportedSecurityTypes = new HashSet <SecurityType> { SecurityType.Equity, SecurityType.Forex, SecurityType.Cfd, SecurityType.Option, SecurityType.Future, SecurityType.Crypto }; var minResolution = new Lazy <Resolution>(() => algorithm.Securities.Select(x => x.Value.Resolution).DefaultIfEmpty(Resolution.Second).Min()); Log.Trace("BrokerageSetupHandler.Setup(): Fetching open orders from brokerage..."); try { GetOpenOrders(algorithm, parameters.ResultHandler, parameters.TransactionHandler, brokerage, supportedSecurityTypes, minResolution.Value); } catch (Exception err) { Log.Error(err); AddInitializationError("Error getting open orders from brokerage: " + err.Message, err); return(false); } Log.Trace("BrokerageSetupHandler.Setup(): Fetching holdings from brokerage..."); try { var utcNow = DateTime.UtcNow; // populate the algorithm with the account's current holdings var holdings = brokerage.GetAccountHoldings(); // add options first to ensure raw data normalization mode is set on the equity underlyings foreach (var holding in holdings.OrderByDescending(x => x.Type)) { Log.Trace("BrokerageSetupHandler.Setup(): Has existing holding: " + holding); // verify existing holding security type if (!supportedSecurityTypes.Contains(holding.Type)) { Log.Error("BrokerageSetupHandler.Setup(): Unsupported security type: " + holding.Type + "-" + holding.Symbol.Value); AddInitializationError("Found unsupported security type in existing brokerage holdings: " + holding.Type + ". " + "QuantConnect currently supports the following security types: " + string.Join(",", supportedSecurityTypes)); // keep aggregating these errors continue; } AddUnrequestedSecurity(algorithm, holding.Symbol, minResolution.Value); var security = algorithm.Securities[holding.Symbol]; var exchangeTime = utcNow.ConvertFromUtc(security.Exchange.TimeZone); security.Holdings.SetHoldings(holding.AveragePrice, holding.Quantity); security.SetMarketPrice(new TradeBar { Time = exchangeTime, Open = holding.MarketPrice, High = holding.MarketPrice, Low = holding.MarketPrice, Close = holding.MarketPrice, Volume = 0, Symbol = holding.Symbol, DataType = MarketDataType.TradeBar }); } } catch (Exception err) { Log.Error(err); AddInitializationError("Error getting account holdings from brokerage: " + err.Message, err); return(false); } //Finalize Initialization algorithm.PostInitialize(); BaseSetupHandler.SetupCurrencyConversions(algorithm, parameters.UniverseSelection); if (algorithm.Portfolio.TotalPortfolioValue == 0) { algorithm.Debug("Warning: No cash balances or holdings were found in the brokerage account."); } //Set the starting portfolio value for the strategy to calculate performance: StartingPortfolioValue = algorithm.Portfolio.TotalPortfolioValue; StartingDate = DateTime.Now; // we set the free portfolio value based on the initial total value and the free percentage value algorithm.Settings.FreePortfolioValue = algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage; } catch (Exception err) { AddInitializationError(err.ToString(), err); } finally { if (brokerage != null) { brokerage.Message -= brokerageOnMessage; } } return(Errors.Count == 0); }