public SubmitOrderResult SaveOrder(SubmitOrderRequest request) { var result = new SubmitOrderResult(); orderNumber = request.OrderNumber; try { trans = cn.BeginTransaction(); var orderId = InsertOrderRecord(request); foreach (var orderLineItem in request.LineItems) { InsertLineItems(orderId, orderLineItem); } trans.Commit(); trans.Dispose(); trans = null; result = GetOrderResults(); } catch (SqlException ex) { result.HasException = true; result.Exception = ExceptionFactory.BuildSqlException(ex); } catch (Exception ex) { result.HasException = true; result.Exception = ExceptionFactory.BuildSystemException(ex); } finally { if (trans != null) { trans.Rollback(); trans.Dispose(); } cn.Close(); cn.Dispose(); } return result; }
public void GetOpenOrdersWorksForSubmittedFilledStatus() { // Initializes the transaction handler var transactionHandler = new BrokerageTransactionHandler(); var broker = new BacktestingBrokerage(_algorithm); transactionHandler.Initialize(_algorithm, broker, new BacktestingResultHandler()); // Creates a limit order var security = _algorithm.Securities[Ticker]; var price = 1.12m; security.SetMarketPrice(new Tick(DateTime.Now, security.Symbol, price, price, price)); var orderRequest = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, 1000, 0, 1.11m, DateTime.Now, ""); // Mock the the order processor var orderProcessorMock = new Mock <IOrderProcessor>(); orderProcessorMock.Setup(m => m.GetOrderTicket(It.IsAny <int>())).Returns(new OrderTicket(_algorithm.Transactions, orderRequest)); _algorithm.Transactions.SetOrderProcessor(orderProcessorMock.Object); Assert.AreEqual(transactionHandler.GetOpenOrders().Count, 0); // Submit and process a limit order var orderTicket = transactionHandler.Process(orderRequest); transactionHandler.HandleOrderRequest(orderRequest); Assert.AreEqual(orderTicket.Status, OrderStatus.Submitted); var openOrders = transactionHandler.GetOpenOrders(); Assert.AreEqual(openOrders.Count, 1); Assert.AreEqual(openOrders[0].Id, orderTicket.OrderId); broker.Scan(); Assert.AreEqual(orderTicket.Status, OrderStatus.Filled); Assert.AreEqual(transactionHandler.GetOpenOrders().Count, 0); }
public void RoundOff_LessThanLotSize_Fractional_Orders() { var algo = new QCAlgorithm(); algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo)); algo.SetBrokerageModel(BrokerageName.Default); algo.SetCash(100000); // Sets the Security var security = algo.AddSecurity(SecurityType.Crypto, "BTCUSD", Resolution.Hour, Market.GDAX, false, 3.3m, true); //Initializes the transaction handler var transactionHandler = new BrokerageTransactionHandler(); transactionHandler.Initialize(algo, new BacktestingBrokerage(algo), new BacktestingResultHandler()); // Creates the order var orderRequest = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, 0.000000009m, 0, 0, DateTime.Now, ""); var order = Order.CreateOrder(orderRequest); var actual = transactionHandler.RoundOffOrder(order, security); Assert.AreEqual(0, actual); }
private int InsertOrderRecord(SubmitOrderRequest request) { using (var cmd = new SqlCommand("usp_InsertOrder", cn, trans)) { var parameters = new [] { new SqlParameter("@OrderNumber", SqlDbType.NVarChar, 255) { Value = request.OrderNumber }, new SqlParameter("@Account", SqlDbType.NVarChar, 16) { Value = request.Account }, new SqlParameter("@FirstName", SqlDbType.NVarChar, 64) { Value = request.FirstName }, new SqlParameter("@LastName", SqlDbType.NVarChar, 64) { Value = request.LastName }, new SqlParameter("@AddressLine1", SqlDbType.NVarChar, 255) { Value = request.Address.Line1 }, new SqlParameter("@AddressLine2", SqlDbType.NVarChar, 255) { Value = request.Address.Line2 }, new SqlParameter("@City", SqlDbType.NVarChar, 64) { Value = request.Address.City }, new SqlParameter("@ST", SqlDbType.NVarChar, 2) { Value = request.Address.State }, new SqlParameter("@ZipCode", SqlDbType.NVarChar, 16) { Value = request.Address.Zip }, new SqlParameter("@Phone", SqlDbType.NVarChar, 16) { Value = request.Phone } }; var prmId = new SqlParameter("@ID", SqlDbType.Int) { Direction = ParameterDirection.Output }; cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddRange(parameters); cmd.Parameters.Add(prmId); cmd.ExecuteNonQuery(); id = Convert.ToInt32(prmId.Value); } return(id); }
/// <summary> /// Perform preorder checks to ensure we have sufficient capital, /// the market is open, and we haven't exceeded maximum realistic orders per day. /// </summary> /// <returns>OrderResponse. If no error, order request is submitted.</returns> private OrderResponse PreOrderChecksImpl(SubmitOrderRequest request) { //Most order methods use security objects; so this isn't really used. // todo: Left here for now but should review Security security; if (!Securities.TryGetValue(request.Symbol, out security)) { return(OrderResponse.Error(request, OrderResponseErrorCode.MissingSecurity, "You haven't requested " + request.Symbol.ToString() + " data. Add this with AddSecurity() in the Initialize() Method.")); } //Ordering 0 is useless. if (request.Quantity == 0 || request.Symbol == null || request.Symbol == QuantConnect.Symbol.Empty || Math.Abs(request.Quantity) < security.SymbolProperties.LotSize) { return(OrderResponse.ZeroQuantity(request)); } if (!security.IsTradable) { return(OrderResponse.Error(request, OrderResponseErrorCode.NonTradableSecurity, "The security with symbol '" + request.Symbol.ToString() + "' is marked as non-tradable.")); } var price = security.Price; //Check the exchange is open before sending a market on close orders if (request.OrderType == OrderType.MarketOnClose && !security.Exchange.ExchangeOpen) { return(OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.")); } //Check the exchange is open before sending a exercise orders if (request.OrderType == OrderType.OptionExercise && !security.Exchange.ExchangeOpen) { return(OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.")); } if (price == 0) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityPriceZero, request.Symbol.ToString() + ": asset price is $0. If using custom data make sure you've set the 'Value' property.")); } // check quote currency existence/conversion rate on all orders Cash quoteCash; var quoteCurrency = security.QuoteCurrency.Symbol; if (!Portfolio.CashBook.TryGetValue(quoteCurrency, out quoteCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.QuoteCurrencyRequired, request.Symbol.Value + ": requires " + quoteCurrency + " in the cashbook to trade.")); } if (security.QuoteCurrency.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ConversionRateZero, request.Symbol.Value + ": requires " + quoteCurrency + " to have a non-zero conversion rate. This can be caused by lack of data.")); } // need to also check base currency existence/conversion rate on forex orders if (security.Type == SecurityType.Forex) { Cash baseCash; var baseCurrency = ((Forex)security).BaseCurrencySymbol; if (!Portfolio.CashBook.TryGetValue(baseCurrency, out baseCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " in the cashbook to trade.")); } if (baseCash.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " to have non-zero conversion rates. This can be caused by lack of data.")); } } //Make sure the security has some data: if (!security.HasData) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData, "There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point.")); } //We've already processed too many orders: max 100 per day or the memory usage explodes if (Transactions.OrdersCount > _maxOrders) { Status = AlgorithmStatus.Stopped; return(OrderResponse.Error(request, OrderResponseErrorCode.ExceededMaximumOrders, string.Format("You have exceeded maximum number of orders ({0}), for unlimited orders upgrade your account.", _maxOrders))); } if (request.OrderType == OrderType.OptionExercise) { if (security.Type != SecurityType.Option) { return(OrderResponse.Error(request, OrderResponseErrorCode.NonExercisableSecurity, "The security with symbol '" + request.Symbol.ToString() + "' is not exercisable.")); } if (security.Holdings.IsShort) { return(OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType, "The security with symbol '" + request.Symbol.ToString() + "' has a short option position. Only long option positions are exercisable.")); } if (request.Quantity > security.Holdings.Quantity) { return(OrderResponse.Error(request, OrderResponseErrorCode.UnsupportedRequestType, "Cannot exercise more contracts of '" + request.Symbol.ToString() + "' than is currently available in the portfolio. ")); } if (request.Quantity <= 0.0m) { OrderResponse.ZeroQuantity(request); } } if (request.OrderType == OrderType.MarketOnClose) { var nextMarketClose = security.Exchange.Hours.GetNextMarketClose(security.LocalTime, false); // must be submitted with at least 10 minutes in trading day, add buffer allow order submission var latestSubmissionTime = nextMarketClose.AddMinutes(-15.50); if (!security.Exchange.ExchangeOpen || Time > latestSubmissionTime) { // tell the user we require a 16 minute buffer, on minute data in live a user will receive the 3:44->3:45 bar at 3:45, // this is already too late to submit one of these orders, so make the user do it at the 3:43->3:44 bar so it's submitted // to the brokerage before 3:45. return(OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate, "MarketOnClose orders must be placed with at least a 16 minute buffer before market close.")); } } // passes all initial order checks return(OrderResponse.Success(request)); }
public void SendingNewOrderFromOnOrderEvent() { //Initializes the transaction handler var transactionHandler = new BacktestingTransactionHandler(); var brokerage = new BacktestingBrokerage(_algorithm); transactionHandler.Initialize(_algorithm, brokerage, new BacktestingResultHandler()); // Creates a market order var security = _algorithm.Securities[Ticker]; var price = 1.12m; security.SetMarketPrice(new Tick(DateTime.UtcNow.AddDays(-1), security.Symbol, price, price, price)); var orderRequest = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, 1000, 0, 0, 0, DateTime.UtcNow, ""); var orderRequest2 = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, -1000, 0, 0, 0, DateTime.UtcNow, ""); orderRequest.SetOrderId(1); orderRequest2.SetOrderId(2); // Mock the the order processor var orderProcessorMock = new Mock <IOrderProcessor>(); orderProcessorMock.Setup(m => m.GetOrderTicket(It.Is <int>(i => i == 1))).Returns(new OrderTicket(_algorithm.Transactions, orderRequest)); orderProcessorMock.Setup(m => m.GetOrderTicket(It.Is <int>(i => i == 2))).Returns(new OrderTicket(_algorithm.Transactions, orderRequest2)); _algorithm.Transactions.SetOrderProcessor(orderProcessorMock.Object); var orderEventCalls = 0; brokerage.OrderStatusChanged += (sender, orderEvent) => { orderEventCalls++; switch (orderEventCalls) { case 1: Assert.AreEqual(1, orderEvent.OrderId); Assert.AreEqual(OrderStatus.Submitted, orderEvent.Status); // we send a new order request var ticket2 = transactionHandler.Process(orderRequest2); break; case 2: Assert.AreEqual(2, orderEvent.OrderId); Assert.AreEqual(OrderStatus.Submitted, orderEvent.Status); break; case 3: Assert.AreEqual(1, orderEvent.OrderId); Assert.AreEqual(OrderStatus.Filled, orderEvent.Status); break; case 4: Assert.AreEqual(2, orderEvent.OrderId); Assert.AreEqual(OrderStatus.Filled, orderEvent.Status); break; } Log.Trace($"{orderEvent}"); }; var ticket = transactionHandler.Process(orderRequest); Assert.IsTrue(orderRequest.Response.IsProcessed); Assert.IsTrue(orderRequest.Response.IsSuccess); Assert.AreEqual(OrderRequestStatus.Processed, orderRequest.Status); Assert.IsTrue(orderRequest2.Response.IsProcessed); Assert.IsTrue(orderRequest2.Response.IsSuccess); Assert.AreEqual(OrderRequestStatus.Processed, orderRequest2.Status); var order1 = transactionHandler.GetOrderById(1); Assert.AreEqual(OrderStatus.Filled, order1.Status); var order2 = transactionHandler.GetOrderById(2); Assert.AreEqual(OrderStatus.Filled, order2.Status); // 2 submitted and 2 filled Assert.AreEqual(4, orderEventCalls); }
/// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice data) { if (!data.Bars.ContainsKey(symbol)) { return; } // each month make an action if (Time.Month != LastMonth) { // we'll submit the next type of order from the queue var orderType = _orderTypesQueue.Dequeue(); //Log(""); Log("\r\n--------------MONTH: " + Time.ToString("MMMM") + ":: " + orderType + "\r\n"); //Log(""); LastMonth = Time.Month; Log("ORDER TYPE:: " + orderType); var isLong = Quantity > 0; var stopPrice = isLong ? (1 + StopPercentage) * data.Bars[symbol].High : (1 - StopPercentage) * data.Bars[symbol].Low; var limitPrice = isLong ? (1 - LimitPercentage) * stopPrice : (1 + LimitPercentage) * stopPrice; if (orderType == OrderType.Limit) { limitPrice = !isLong ? (1 + LimitPercentage) * data.Bars[symbol].High : (1 - LimitPercentage) * data.Bars[symbol].Low; } var request = new SubmitOrderRequest(orderType, SecType, symbol, Quantity, stopPrice, limitPrice, Time, orderType.ToString()); var ticket = Transactions.AddOrder(request); _tickets.Add(ticket); } else if (_tickets.Count > 0) { var ticket = _tickets.Last(); if (Time.Day > 8 && Time.Day < 14) { if (ticket.UpdateRequests.Count == 0 && ticket.Status.IsOpen()) { Log("TICKET:: " + ticket); ticket.Update(new UpdateOrderFields { Quantity = ticket.Quantity + Math.Sign(Quantity) * DeltaQuantity, Tag = "Change quantity: " + Time }); Log("UPDATE1:: " + ticket.UpdateRequests.Last()); } } else if (Time.Day > 13 && Time.Day < 20) { if (ticket.UpdateRequests.Count == 1 && ticket.Status.IsOpen()) { Log("TICKET:: " + ticket); ticket.Update(new UpdateOrderFields { LimitPrice = Security.Price * (1 - Math.Sign(ticket.Quantity) * LimitPercentageDelta), StopPrice = Security.Price * (1 + Math.Sign(ticket.Quantity) * StopPercentageDelta), Tag = "Change prices: " + Time }); Log("UPDATE2:: " + ticket.UpdateRequests.Last()); } } else { if (ticket.UpdateRequests.Count == 2 && ticket.Status.IsOpen()) { Log("TICKET:: " + ticket); ticket.Cancel(Time + " and is still open!"); Log("CANCELLED:: " + ticket.CancelRequest); } } } }
public SubmitOrderResult SubmitOrder(SubmitOrderRequest request) { var submitter = new SubmitOrderHandler(connectString); return(submitter.SaveOrder(request)); }
public void OrdersAreSubmittedImmediatelyForTargetsToExecute( Language language, double[] historicalPrices, decimal openOrdersQuantity, int expectedOrdersSubmitted, decimal expectedTotalQuantity) { var actualOrdersSubmitted = new List <SubmitOrderRequest>(); var time = new DateTime(2018, 8, 2, 16, 0, 0); var historyProvider = new Mock <IHistoryProvider>(); historyProvider.Setup(m => m.GetHistory(It.IsAny <IEnumerable <HistoryRequest> >(), It.IsAny <DateTimeZone>())) .Returns(historicalPrices.Select((x, i) => new Slice(time.AddMinutes(i), new List <BaseData> { new TradeBar { Time = time.AddMinutes(i), Symbol = Symbols.AAPL, Open = Convert.ToDecimal(x), High = Convert.ToDecimal(x), Low = Convert.ToDecimal(x), Close = Convert.ToDecimal(x), Volume = 100m } }))); var algorithm = new QCAlgorithm(); algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm)); algorithm.SetPandasConverter(); algorithm.SetHistoryProvider(historyProvider.Object); algorithm.SetDateTime(time.AddMinutes(5)); var security = algorithm.AddEquity(Symbols.AAPL.Value); security.SetMarketPrice(new TradeBar { Value = 250 }); algorithm.SetFinishedWarmingUp(); var openOrderRequest = new SubmitOrderRequest(OrderType.Market, SecurityType.Equity, Symbols.AAPL, openOrdersQuantity, 0, 0, DateTime.MinValue, ""); openOrderRequest.SetOrderId(1); var openOrderTicket = new OrderTicket(algorithm.Transactions, openOrderRequest); var orderProcessor = new Mock <IOrderProcessor>(); orderProcessor.Setup(m => m.Process(It.IsAny <SubmitOrderRequest>())) .Returns((SubmitOrderRequest request) => new OrderTicket(algorithm.Transactions, request)) .Callback((OrderRequest request) => actualOrdersSubmitted.Add((SubmitOrderRequest)request)); orderProcessor.Setup(m => m.GetOpenOrders(It.IsAny <Func <Order, bool> >())) .Returns(new List <Order> { new MarketOrder(Symbols.AAPL, openOrdersQuantity, DateTime.MinValue) }); orderProcessor.Setup(m => m.GetOpenOrderTickets(It.IsAny <Func <OrderTicket, bool> >())) .Returns(new List <OrderTicket> { openOrderTicket }); algorithm.Transactions.SetOrderProcessor(orderProcessor.Object); var model = GetExecutionModel(language); algorithm.SetExecution(model); var changes = SecurityChangesTests.CreateNonInternal(new[] { security }, Enumerable.Empty <Security>()); model.OnSecuritiesChanged(algorithm, changes); var targets = new IPortfolioTarget[] { new PortfolioTarget(Symbols.AAPL, 10) }; model.Execute(algorithm, targets); Assert.AreEqual(expectedOrdersSubmitted, actualOrdersSubmitted.Count); Assert.AreEqual(expectedTotalQuantity, actualOrdersSubmitted.Sum(x => x.Quantity)); if (actualOrdersSubmitted.Count == 1) { var request = actualOrdersSubmitted[0]; Assert.AreEqual(expectedTotalQuantity, request.Quantity); Assert.AreEqual(algorithm.UtcTime, request.Time); } }
/// <summary> /// Handles a request to submit a new order /// </summary> private OrderResponse HandleSubmitOrderRequest(SubmitOrderRequest request) { OrderTicket ticket; var order = Order.CreateOrder(request); // ensure the order is tagged with a currency var security = _algorithm.Securities[order.Symbol]; order.PriceCurrency = security.SymbolProperties.QuoteCurrency; // rounds off the order towards 0 to the nearest multiple of lot size order.Quantity = RoundOffOrder(order, security); if (!_orders.TryAdd(order.Id, order)) { Log.Error("BrokerageTransactionHandler.HandleSubmitOrderRequest(): Unable to add new order, order not processed."); return(OrderResponse.Error(request, OrderResponseErrorCode.OrderAlreadyExists, "Cannot process submit request because order with id {0} already exists")); } if (!_orderTickets.TryGetValue(order.Id, out ticket)) { Log.Error("BrokerageTransactionHandler.HandleSubmitOrderRequest(): Unable to retrieve order ticket, order not processed."); return(OrderResponse.UnableToFindOrder(request)); } // rounds the order prices RoundOrderPrices(order, security); // save current security time and prices order.OrderSubmissionData = new OrderSubmissionData(security.GetLastData()); // update the ticket's internal storage with this new order reference ticket.SetOrder(order); if (order.Quantity == 0) { order.Status = OrderStatus.Invalid; var response = OrderResponse.ZeroQuantity(request); _algorithm.Error(response.ErrorMessage); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "Unable to add order for zero quantity")); return(response); } // check to see if we have enough money to place the order HasSufficientBuyingPowerForOrderResult hasSufficientBuyingPowerResult; try { hasSufficientBuyingPowerResult = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(_algorithm.Portfolio, security, order); } catch (Exception err) { Log.Error(err); _algorithm.Error(string.Format("Order Error: id: {0}, Error executing margin models: {1}", order.Id, err.Message)); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "Error executing margin models")); return(OrderResponse.Error(request, OrderResponseErrorCode.ProcessingError, "Error in GetSufficientCapitalForOrder")); } if (!hasSufficientBuyingPowerResult.IsSufficient) { order.Status = OrderStatus.Invalid; var errorMessage = $"Order Error: id: {order.Id}, Insufficient buying power to complete order (Value:{order.GetValue(security).SmartRounding()}), Reason: {hasSufficientBuyingPowerResult.Reason}"; var response = OrderResponse.Error(request, OrderResponseErrorCode.InsufficientBuyingPower, errorMessage); _algorithm.Error(response.ErrorMessage); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, errorMessage)); return(response); } // verify that our current brokerage can actually take the order BrokerageMessageEvent message; if (!_algorithm.BrokerageModel.CanSubmitOrder(security, order, out message)) { // if we couldn't actually process the order, mark it as invalid and bail order.Status = OrderStatus.Invalid; if (message == null) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidOrder", "BrokerageModel declared unable to submit order: " + order.Id); } var response = OrderResponse.Error(request, OrderResponseErrorCode.BrokerageModelRefusedToSubmitOrder, "OrderID: " + order.Id + " " + message); _algorithm.Error(response.ErrorMessage); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "BrokerageModel declared unable to submit order")); return(response); } // set the order status based on whether or not we successfully submitted the order to the market bool orderPlaced; try { orderPlaced = _brokerage.PlaceOrder(order); } catch (Exception err) { Log.Error(err); orderPlaced = false; } if (!orderPlaced) { // we failed to submit the order, invalidate it order.Status = OrderStatus.Invalid; var errorMessage = "Brokerage failed to place order: " + order.Id; var response = OrderResponse.Error(request, OrderResponseErrorCode.BrokerageFailedToSubmitOrder, errorMessage); _algorithm.Error(response.ErrorMessage); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "Brokerage failed to place order")); return(response); } return(OrderResponse.Success(request)); }
public PaymentOptionDTO CreatePaymentOption(Models.PaymentMethod paymentMethod, SubmitOrderRequest request) { return(new PaymentOptionDTO() { PaymentOptionName = paymentMethod.ShortClassName, PONumber = request?.PaymentMethod?.Invoice }); }
public Task <SubmitOrderResponse> SubmitOrderAsync(SubmitOrderRequest request) { return(HandleAsync <SubmitOrderRequest, SubmitOrderResponse> (request)); }
/// <summary> /// Liquidate option contact holdings who's underlying security has split /// </summary> private void ProcessSplitSymbols(IAlgorithm algorithm, List <Split> splitWarnings, List <Delisting> pendingDelistings) { // NOTE: This method assumes option contracts have the same core trading hours as their underlying contract // This is a small performance optimization to prevent scanning every contract on every time step, // instead we scan just the underlyings, thereby reducing the time footprint of this methods by a factor // of N, the number of derivative subscriptions for (int i = splitWarnings.Count - 1; i >= 0; i--) { var split = splitWarnings[i]; var security = algorithm.Securities[split.Symbol]; if (!security.IsTradable && !algorithm.UniverseManager.ActiveSecurities.Keys.Contains(split.Symbol)) { Log.Debug($"AlgorithmManager.ProcessSplitSymbols(): {_algorithm.Time} - Removing split warning for {security.Symbol}"); // remove the warning from out list splitWarnings.RemoveAt(i); // Since we are storing the split warnings for a loop // we need to check if the security was removed. // When removed, it will be marked as non tradable but just in case // we expect it not to be an active security either continue; } var nextMarketClose = security.Exchange.Hours.GetNextMarketClose(security.LocalTime, false); // determine the latest possible time we can submit a MOC order var configs = algorithm.SubscriptionManager.SubscriptionDataConfigService .GetSubscriptionDataConfigs(security.Symbol); if (configs.Count == 0) { // should never happen at this point, if it does let's give some extra info throw new Exception( $"AlgorithmManager.ProcessSplitSymbols(): {_algorithm.Time} - No subscriptions found for {security.Symbol}" + $", IsTradable: {security.IsTradable}" + $", Active: {algorithm.UniverseManager.ActiveSecurities.Keys.Contains(split.Symbol)}"); } var latestMarketOnCloseTimeRoundedDownByResolution = nextMarketClose.Subtract(MarketOnCloseOrder.SubmissionTimeBuffer) .RoundDownInTimeZone(configs.GetHighestResolution().ToTimeSpan(), security.Exchange.TimeZone, configs.First().DataTimeZone); // we don't need to do anyhing until the market closes if (security.LocalTime < latestMarketOnCloseTimeRoundedDownByResolution) { continue; } // fetch all option derivatives of the underlying with holdings (excluding the canonical security) var derivatives = algorithm.Securities.Where(kvp => kvp.Key.HasUnderlying && kvp.Key.SecurityType.IsOption() && kvp.Key.Underlying == security.Symbol && !kvp.Key.Underlying.IsCanonical() && kvp.Value.HoldStock ); foreach (var kvp in derivatives) { var optionContractSymbol = kvp.Key; var optionContractSecurity = (Option)kvp.Value; if (pendingDelistings.Any(x => x.Symbol == optionContractSymbol && x.Time.Date == optionContractSecurity.LocalTime.Date)) { // if the option is going to be delisted today we skip sending the market on close order continue; } // close any open orders algorithm.Transactions.CancelOpenOrders(optionContractSymbol, "Canceled due to impending split. Separate MarketOnClose order submitted to liquidate position."); var request = new SubmitOrderRequest(OrderType.MarketOnClose, optionContractSecurity.Type, optionContractSymbol, -optionContractSecurity.Holdings.Quantity, 0, 0, algorithm.UtcTime, "Liquidated due to impending split. Option splits are not currently supported." ); // send MOC order to liquidate option contract holdings algorithm.Transactions.AddOrder(request); // mark option contract as not tradable optionContractSecurity.IsTradable = false; algorithm.Debug($"MarketOnClose order submitted for option contract '{optionContractSymbol}' due to impending {split.Symbol.Value} split event. " + "Option splits are not currently supported."); } // remove the warning from out list splitWarnings.RemoveAt(i); } }
public async Task <ActionResult <SubmitOrderResponse> > SubmitOrderAsync(SubmitOrderRequest request) { var response = await Service.SubmitOrderAsync(request); return(Ok(response)); }
public SubmitOrderResponse SubmitOrderClient(SubmitOrderRequest submitOrderRequest) { var submitOrderResponse = client.SubmitOrder(submitOrderRequest); return(submitOrderResponse); }
/// <summary> /// Launch the algorithm manager to run this strategy /// </summary> /// <param name="job">Algorithm job</param> /// <param name="algorithm">Algorithm instance</param> /// <param name="feed">Datafeed object</param> /// <param name="transactions">Transaction manager object</param> /// <param name="results">Result handler object</param> /// <param name="realtime">Realtime processing object</param> /// <param name="commands">The command queue for relaying extenal commands to the algorithm</param> /// <param name="token">Cancellation token</param> /// <remarks>Modify with caution</remarks> public void Run(AlgorithmNodePacket job, IAlgorithm algorithm, IDataFeed feed, ITransactionHandler transactions, IResultHandler results, IRealTimeHandler realtime, ICommandQueueHandler commands, CancellationToken token) { //Initialize: _dataPointCount = 0; _algorithm = algorithm; var portfolioValue = algorithm.Portfolio.TotalPortfolioValue; var backtestMode = (job.Type == PacketType.BacktestNode); var methodInvokers = new Dictionary <Type, MethodInvoker>(); var marginCallFrequency = TimeSpan.FromMinutes(5); var nextMarginCallTime = DateTime.MinValue; var settlementScanFrequency = TimeSpan.FromMinutes(30); var nextSettlementScanTime = DateTime.MinValue; var delistings = new List <Delisting>(); //Initialize Properties: _algorithmId = job.AlgorithmId; _algorithm.Status = AlgorithmStatus.Running; _previousTime = algorithm.StartDate.Date; //Create the method accessors to push generic types into algorithm: Find all OnData events: // Algorithm 2.0 data accessors var hasOnDataTradeBars = AddMethodInvoker <TradeBars>(algorithm, methodInvokers); var hasOnDataQuoteBars = AddMethodInvoker <QuoteBars>(algorithm, methodInvokers); var hasOnDataOptionChains = AddMethodInvoker <OptionChains>(algorithm, methodInvokers); var hasOnDataTicks = AddMethodInvoker <Ticks>(algorithm, methodInvokers); // dividend and split events var hasOnDataDividends = AddMethodInvoker <Dividends>(algorithm, methodInvokers); var hasOnDataSplits = AddMethodInvoker <Splits>(algorithm, methodInvokers); var hasOnDataDelistings = AddMethodInvoker <Delistings>(algorithm, methodInvokers); var hasOnDataSymbolChangedEvents = AddMethodInvoker <SymbolChangedEvents>(algorithm, methodInvokers); // Algorithm 3.0 data accessors var hasOnDataSlice = algorithm.GetType().GetMethods() .Where(x => x.Name == "OnData" && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(Slice)) .FirstOrDefault(x => x.DeclaringType == algorithm.GetType()) != null; //Go through the subscription types and create invokers to trigger the event handlers for each custom type: foreach (var config in algorithm.SubscriptionManager.Subscriptions) { //If type is a custom feed, check for a dedicated event handler if (config.IsCustomData) { //Get the matching method for this event handler - e.g. public void OnData(Quandl data) { .. } var genericMethod = (algorithm.GetType()).GetMethod("OnData", new[] { config.Type }); //If we already have this Type-handler then don't add it to invokers again. if (methodInvokers.ContainsKey(config.Type)) { continue; } //If we couldnt find the event handler, let the user know we can't fire that event. if (genericMethod == null && !hasOnDataSlice) { algorithm.RunTimeError = new Exception("Data event handler not found, please create a function matching this template: public void OnData(" + config.Type.Name + " data) { }"); _algorithm.Status = AlgorithmStatus.RuntimeError; return; } if (genericMethod != null) { methodInvokers.Add(config.Type, genericMethod.DelegateForCallMethod()); } } } //Loop over the queues: get a data collection, then pass them all into relevent methods in the algorithm. Log.Trace("AlgorithmManager.Run(): Begin DataStream - Start: " + algorithm.StartDate + " Stop: " + algorithm.EndDate); foreach (var timeSlice in Stream(job, algorithm, feed, results, token)) { // reset our timer on each loop _currentTimeStepTime = DateTime.UtcNow; //Check this backtest is still running: if (_algorithm.Status != AlgorithmStatus.Running) { Log.Error(string.Format("AlgorithmManager.Run(): Algorithm state changed to {0} at {1}", _algorithm.Status, timeSlice.Time)); break; } //Execute with TimeLimit Monitor: if (token.IsCancellationRequested) { Log.Error("AlgorithmManager.Run(): CancellationRequestion at " + timeSlice.Time); return; } // before doing anything, check our command queue foreach (var command in commands.GetCommands()) { if (command == null) { continue; } Log.Trace("AlgorithmManager.Run(): Executing {0}", command); CommandResultPacket result; try { result = command.Run(algorithm); } catch (Exception err) { Log.Error(err); algorithm.Error(string.Format("{0} Error: {1}", command.GetType().Name, err.Message)); result = new CommandResultPacket(command, false); } // send the result of the command off to the result handler results.Messages.Enqueue(result); } var time = timeSlice.Time; _dataPointCount += timeSlice.DataPointCount; //If we're in backtest mode we need to capture the daily performance. We do this here directly //before updating the algorithm state with the new data from this time step, otherwise we'll //produce incorrect samples (they'll take into account this time step's new price values) if (backtestMode) { //On day-change sample equity and daily performance for statistics calculations if (_previousTime.Date != time.Date) { SampleBenchmark(algorithm, results, _previousTime.Date); //Sample the portfolio value over time for chart. results.SampleEquity(_previousTime, Math.Round(algorithm.Portfolio.TotalPortfolioValue, 4)); //Check for divide by zero if (portfolioValue == 0m) { results.SamplePerformance(_previousTime.Date, 0); } else { results.SamplePerformance(_previousTime.Date, Math.Round((algorithm.Portfolio.TotalPortfolioValue - portfolioValue) * 100 / portfolioValue, 10)); } portfolioValue = algorithm.Portfolio.TotalPortfolioValue; } } else { // live mode continously sample the benchmark SampleBenchmark(algorithm, results, time); } //Update algorithm state after capturing performance from previous day //Set the algorithm and real time handler's time algorithm.SetDateTime(time); if (timeSlice.Slice.SymbolChangedEvents.Count != 0) { if (hasOnDataSymbolChangedEvents) { methodInvokers[typeof(SymbolChangedEvents)](algorithm, timeSlice.Slice.SymbolChangedEvents); } foreach (var symbol in timeSlice.Slice.SymbolChangedEvents.Keys) { // cancel all orders for the old symbol foreach (var ticket in transactions.GetOrderTickets(x => x.Status.IsOpen() && x.Symbol == symbol)) { ticket.Cancel("Open order cancelled on symbol changed event"); } } } if (timeSlice.SecurityChanges != SecurityChanges.None) { foreach (var security in timeSlice.SecurityChanges.AddedSecurities) { if (!algorithm.Securities.ContainsKey(security.Symbol)) { // add the new security algorithm.Securities.Add(security); } } } //On each time step push the real time prices to the cashbook so we can have updated conversion rates foreach (var update in timeSlice.CashBookUpdateData) { var cash = update.Target; foreach (var data in update.Data) { cash.Update(data); } } //Update the securities properties: first before calling user code to avoid issues with data foreach (var update in timeSlice.SecuritiesUpdateData) { var security = update.Target; foreach (var data in update.Data) { security.SetMarketPrice(data); } // Send market price updates to the TradeBuilder algorithm.TradeBuilder.SetMarketPrice(security.Symbol, security.Price); } // fire real time events after we've updated based on the new data realtime.SetTime(timeSlice.Time); // process fill models on the updated data before entering algorithm, applies to all non-market orders transactions.ProcessSynchronousEvents(); if (delistings.Count != 0) { for (int i = 0; i < delistings.Count; i++) { var symbol = delistings[i].Symbol; var ticket = delistings[i].Ticket; var security = algorithm.Securities[symbol]; if (ticket != null && ticket.Status == OrderStatus.Filled) { // If invested after market on close order is filled, liquidate if (security.Invested) { algorithm.Liquidate(symbol); } delistings.RemoveAt(i--); } // Submit an order to liquidate on market close when invested after the delisting warning if (ticket == null && security.Invested) { var submitOrderRequest = new SubmitOrderRequest(OrderType.MarketOnClose, security.Type, security.Symbol, -security.Holdings.Quantity, 0, 0, algorithm.UtcTime, "Liquidate from delisting"); ticket = algorithm.Transactions.ProcessRequest(submitOrderRequest); delistings[i].SetOrderTicket(ticket); } } } //Check if the user's signalled Quit: loop over data until day changes. if (algorithm.Status == AlgorithmStatus.Stopped) { Log.Trace("AlgorithmManager.Run(): Algorithm quit requested."); break; } if (algorithm.RunTimeError != null) { _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Trace(string.Format("AlgorithmManager.Run(): Algorithm encountered a runtime error at {0}. Error: {1}", timeSlice.Time, algorithm.RunTimeError)); break; } // perform margin calls, in live mode we can also use realtime to emit these if (time >= nextMarginCallTime || (_liveMode && nextMarginCallTime > DateTime.UtcNow)) { // determine if there are possible margin call orders to be executed bool issueMarginCallWarning; var marginCallOrders = algorithm.Portfolio.ScanForMarginCall(out issueMarginCallWarning); if (marginCallOrders.Count != 0) { var executingMarginCall = false; try { // tell the algorithm we're about to issue the margin call algorithm.OnMarginCall(marginCallOrders); executingMarginCall = true; // execute the margin call orders var executedTickets = algorithm.Portfolio.MarginCallModel.ExecuteMarginCall(marginCallOrders); foreach (var ticket in executedTickets) { algorithm.Error(string.Format("{0} - Executed MarginCallOrder: {1} - Quantity: {2} @ {3}", algorithm.Time, ticket.Symbol, ticket.Quantity, ticket.AverageFillPrice)); } } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; var locator = executingMarginCall ? "Portfolio.MarginCallModel.ExecuteMarginCall" : "OnMarginCall"; Log.Error(string.Format("AlgorithmManager.Run(): RuntimeError: {0}: ", locator) + err); return; } } // we didn't perform a margin call, but got the warning flag back, so issue the warning to the algorithm else if (issueMarginCallWarning) { try { algorithm.OnMarginCallWarning(); } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: OnMarginCallWarning: " + err); return; } } nextMarginCallTime = time + marginCallFrequency; } // perform check for settlement of unsettled funds if (time >= nextSettlementScanTime || (_liveMode && nextSettlementScanTime > DateTime.UtcNow)) { algorithm.Portfolio.ScanForCashSettlement(algorithm.UtcTime); nextSettlementScanTime = time + settlementScanFrequency; } // before we call any events, let the algorithm know about universe changes if (timeSlice.SecurityChanges != SecurityChanges.None) { try { algorithm.OnSecuritiesChanged(timeSlice.SecurityChanges); } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: OnSecuritiesChanged event: " + err); return; } } // apply dividends foreach (var dividend in timeSlice.Slice.Dividends.Values) { Log.Trace("AlgorithmManager.Run(): {0}: Applying Dividend for {1}", algorithm.Time, dividend.Symbol.ToString()); algorithm.Portfolio.ApplyDividend(dividend); } // apply splits foreach (var split in timeSlice.Slice.Splits.Values) { try { Log.Trace("AlgorithmManager.Run(): {0}: Applying Split for {1}", algorithm.Time, split.Symbol.ToString()); algorithm.Portfolio.ApplySplit(split); // apply the split to open orders as well in raw mode, all other modes are split adjusted if (_liveMode || algorithm.Securities[split.Symbol].DataNormalizationMode == DataNormalizationMode.Raw) { // in live mode we always want to have our order match the order at the brokerage, so apply the split to the orders var openOrders = transactions.GetOrderTickets(ticket => ticket.Status.IsOpen() && ticket.Symbol == split.Symbol); algorithm.BrokerageModel.ApplySplit(openOrders.ToList(), split); } } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: Split event: " + err); return; } } //Update registered consolidators for this symbol index try { foreach (var update in timeSlice.ConsolidatorUpdateData) { var resolutionTimeSpan = update.Target.Resolution.ToTimeSpan(); var consolidators = update.Target.Consolidators; foreach (var consolidator in consolidators) { foreach (var dataPoint in update.Data) { // Filter out data with resolution higher than the data subscription resolution. // This is needed to avoid feeding in higher resolution data, typically fill-forward bars. // It also prevents volume-based indicators or consolidators summing up volume to generate // invalid values. if (algorithm.UtcTime == dataPoint.EndTime.RoundUp(resolutionTimeSpan).ConvertToUtc(update.Target.ExchangeTimeZone)) { consolidator.Update(dataPoint); } } // scan for time after we've pumped all the data through for this consolidator var localTime = time.ConvertFromUtc(update.Target.ExchangeTimeZone); consolidator.Scan(localTime); } } } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: Consolidators update: " + err); return; } // fire custom event handlers foreach (var update in timeSlice.CustomData) { MethodInvoker methodInvoker; if (!methodInvokers.TryGetValue(update.DataType, out methodInvoker)) { continue; } try { foreach (var dataPoint in update.Data) { if (update.DataType.IsInstanceOfType(dataPoint)) { methodInvoker(algorithm, dataPoint); } } } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: Custom Data: " + err); return; } } try { // fire off the dividend and split events before pricing events if (hasOnDataDividends && timeSlice.Slice.Dividends.Count != 0) { methodInvokers[typeof(Dividends)](algorithm, timeSlice.Slice.Dividends); } if (hasOnDataSplits && timeSlice.Slice.Splits.Count != 0) { methodInvokers[typeof(Splits)](algorithm, timeSlice.Slice.Splits); } if (hasOnDataDelistings && timeSlice.Slice.Delistings.Count != 0) { methodInvokers[typeof(Delistings)](algorithm, timeSlice.Slice.Delistings); } } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: Dividends/Splits/Delistings: " + err); return; } // run the delisting logic after firing delisting events HandleDelistedSymbols(algorithm, timeSlice.Slice.Delistings, delistings); //After we've fired all other events in this second, fire the pricing events: try { // TODO: For backwards compatibility only. Remove in 2017 // For compatibility with Forex Trade data, moving if (timeSlice.Slice.QuoteBars.Count > 0) { foreach (var tradeBar in timeSlice.Slice.QuoteBars.Where(x => x.Key.ID.SecurityType == SecurityType.Forex)) { timeSlice.Slice.Bars.Add(tradeBar.Value.Collapse()); } } if (hasOnDataTradeBars && timeSlice.Slice.Bars.Count > 0) { methodInvokers[typeof(TradeBars)](algorithm, timeSlice.Slice.Bars); } if (hasOnDataQuoteBars && timeSlice.Slice.QuoteBars.Count > 0) { methodInvokers[typeof(QuoteBars)](algorithm, timeSlice.Slice.QuoteBars); } if (hasOnDataOptionChains && timeSlice.Slice.OptionChains.Count > 0) { methodInvokers[typeof(OptionChains)](algorithm, timeSlice.Slice.OptionChains); } if (hasOnDataTicks && timeSlice.Slice.Ticks.Count > 0) { methodInvokers[typeof(Ticks)](algorithm, timeSlice.Slice.Ticks); } } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: New Style Mode: " + err); return; } try { if (timeSlice.Slice.HasData) { // EVENT HANDLER v3.0 -- all data in a single event algorithm.OnData(timeSlice.Slice); } } catch (Exception err) { algorithm.RunTimeError = err; _algorithm.Status = AlgorithmStatus.RuntimeError; Log.Error("AlgorithmManager.Run(): RuntimeError: Slice: " + err); return; } //If its the historical/paper trading models, wait until market orders have been "filled" // Manually trigger the event handler to prevent thread switch. transactions.ProcessSynchronousEvents(); //Save the previous time for the sample calculations _previousTime = time; // Process any required events of the results handler such as sampling assets, equity, or stock prices. results.ProcessSynchronousEvents(); } // End of ForEach feed.Bridge.GetConsumingEnumerable // stop timing the loops _currentTimeStepTime = DateTime.MinValue; //Stream over:: Send the final packet and fire final events: Log.Trace("AlgorithmManager.Run(): Firing On End Of Algorithm..."); try { algorithm.OnEndOfAlgorithm(); } catch (Exception err) { _algorithm.Status = AlgorithmStatus.RuntimeError; algorithm.RunTimeError = new Exception("Error running OnEndOfAlgorithm(): " + err.Message, err.InnerException); Log.Error("AlgorithmManager.OnEndOfAlgorithm(): " + err); return; } // Process any required events of the results handler such as sampling assets, equity, or stock prices. results.ProcessSynchronousEvents(forceProcess: true); //Liquidate Holdings for Calculations: if (_algorithm.Status == AlgorithmStatus.Liquidated && _liveMode) { Log.Trace("AlgorithmManager.Run(): Liquidating algorithm holdings..."); algorithm.Liquidate(); results.LogMessage("Algorithm Liquidated"); results.SendStatusUpdate(AlgorithmStatus.Liquidated); } //Manually stopped the algorithm if (_algorithm.Status == AlgorithmStatus.Stopped) { Log.Trace("AlgorithmManager.Run(): Stopping algorithm..."); results.LogMessage("Algorithm Stopped"); results.SendStatusUpdate(AlgorithmStatus.Stopped); } //Backtest deleted. if (_algorithm.Status == AlgorithmStatus.Deleted) { Log.Trace("AlgorithmManager.Run(): Deleting algorithm..."); results.DebugMessage("Algorithm Id:(" + job.AlgorithmId + ") Deleted by request."); results.SendStatusUpdate(AlgorithmStatus.Deleted); } //Algorithm finished, send regardless of commands: results.SendStatusUpdate(AlgorithmStatus.Completed); //Take final samples: results.SampleRange(algorithm.GetChartUpdates()); results.SampleEquity(_previousTime, Math.Round(algorithm.Portfolio.TotalPortfolioValue, 4)); SampleBenchmark(algorithm, results, _previousTime); //Check for divide by zero if (portfolioValue == 0m) { results.SamplePerformance(_previousTime, 0m); } else { results.SamplePerformance(_previousTime, Math.Round((algorithm.Portfolio.TotalPortfolioValue - portfolioValue) * 100 / portfolioValue, 10)); } } // End of Run();
/// <summary> /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// </summary> /// <param name="data">Slice object keyed by symbol containing the stock data</param> public override void OnData(Slice data) { if (!_security.HasData) { Log("::::: NO DATA :::::"); return; } // each month make an action if (Time.Minute != _lastMinute && Time.Second == 0) { Log(""); Log("--------------Minute: " + Time.Minute); Log(""); _lastMinute = Time.Minute; // we'll submit the next type of order from the queue var orderType = _orderTypesQueue.Dequeue(); Log("ORDER TYPE:: " + orderType); var isLong = _quantity > 0; var stopPrice = isLong ? (1 + StopPercentage) * _security.High : (1 - StopPercentage) * _security.Low; var limitPrice = isLong ? (1 - LimitPercentage) * stopPrice : (1 + LimitPercentage) * stopPrice; if (orderType == OrderType.Limit) { limitPrice = !isLong ? (1 + LimitPercentage) * _security.High : (1 - LimitPercentage) * _security.Low; } var request = new SubmitOrderRequest(orderType, SecType, Securities[_symbol].Symbol, _quantity, stopPrice, limitPrice, Time, orderType.ToString()); var ticket = Transactions.AddOrder(request); _tickets.Add(ticket); if ((decimal)Random.NextDouble() < ImmediateCancelPercentage) { Log("Immediate cancellation requested!"); _immediateCancellations.Add(ticket.OrderId); } } else if (_tickets.Count > 0) { var ticket = _tickets.Last(); if (Time.Second > 15 && Time.Second < 30) { if (ticket.UpdateRequests.Count == 0 && ticket.Status.IsOpen()) { Log(ticket.ToString()); ticket.Update(new UpdateOrderFields { Quantity = ticket.Quantity + Math.Sign(_quantity) * DeltaQuantity, Tag = "Change quantity: " + Time }); Log("UPDATE1:: " + ticket.UpdateRequests.Last()); } } else if (Time.Second > 29 && Time.Second < 45) { if (ticket.UpdateRequests.Count == 1 && ticket.Status.IsOpen()) { Log(ticket.ToString()); ticket.Update(new UpdateOrderFields { LimitPrice = _security.Price * (1 - Math.Sign(ticket.Quantity) * LimitPercentageDelta), StopPrice = _security.Price * (1 + Math.Sign(ticket.Quantity) * StopPercentageDelta), Tag = "Change prices: " + Time }); Log("UPDATE2:: " + ticket.UpdateRequests.Last()); } } else { if (ticket.UpdateRequests.Count == 2 && ticket.Status.IsOpen()) { Log(ticket.ToString()); ticket.Cancel(Time + " and is still open!"); Log("CANCELLED:: " + ticket.CancelRequest); } } } }
public async Task <OrderDTO> GetSubmitOrderData(SubmitOrderRequest request) { Customer customer = kenticoUsers.GetCurrentCustomer(); var notificationEmails = request.EmailConfirmation.Union(new[] { customer.Email }); if ((request?.DeliveryAddress?.Id ?? 0) < 0) { shoppingCart.SetShoppingCartAddress(request.DeliveryAddress); customer.FirstName = request.DeliveryAddress.CustomerName; customer.LastName = string.Empty; customer.Email = request.DeliveryAddress.Email; customer.Phone = request.DeliveryAddress.Phone; } var shippingAddress = shoppingCart.GetCurrentCartShippingAddress(); shippingAddress.Country = localization.GetCountries().FirstOrDefault(c => c.Id == shippingAddress.Country.Id); shippingAddress.State = localization.GetStates().FirstOrDefault(c => c.Id == shippingAddress.State.Id); var billingAddress = shoppingCart.GetDefaultBillingAddress(); var billingState = localization.GetStates().FirstOrDefault(c => c.Id == billingAddress.StateId); var site = siteProvider.GetKenticoSite(); var paymentMethod = shoppingCart.GetPaymentMethod(request.PaymentMethod.Id); var cartItems = shoppingCart.GetShoppingCartItems(); var currency = siteProvider.GetSiteCurrency(); var totals = shoppingCart.GetShoppingCartTotals(); totals.TotalTax = await taxService.EstimateTotalTax(shippingAddress); if (string.IsNullOrWhiteSpace(customer.Company)) { customer.Company = settings.DefaultCustomerCompanyName; } foreach (var item in cartItems.Where(i => i.IsTemplated)) { var taskId = await CallRunGeneratePdfTask(item); item.DesignFilePathTaskId = taskId; } var orderDto = new OrderDTO() { BillingAddress = orderDataFactory.CreateBillingAddress(billingAddress, billingState?.StateDisplayName), ShippingAddress = orderDataFactory.CreateShippingAddress(shippingAddress, customer), Customer = orderDataFactory.CreateCustomer(customer), OrderDate = DateTime.Now, PaymentOption = orderDataFactory.CreatePaymentOption(paymentMethod, request), Site = new SiteDTO() { KenticoSiteID = site.Id, KenticoSiteName = site.Name, ErpCustomerId = site.ErpCustomerId }, OrderCurrency = new CurrencyDTO() { CurrencyCode = currency.Code, KenticoCurrencyID = currency.Id }, OrderStatus = new OrderStatusDTO() { KenticoOrderStatusID = kenticoOrder.GetOrderStatusId("Pending"), OrderStatusName = "PENDING" }, TotalPrice = totals.TotalItemsPrice, TotalShipping = totals.TotalShipping, TotalTax = totals.TotalTax, Items = cartItems.Select(item => MapCartItemTypeToOrderItemType(item)), NotificationsData = notificationEmails.Select(e => new NotificationInfoDto { Email = e, Language = customer.PreferredLanguage }) }; // If only mailing list items in cart, we are not picking any delivery option if (!cartItems.All(i => i.IsMailingList)) { var deliveryMethod = shoppingCart.GetShippingOption(request.DeliveryMethod); orderDto.ShippingOption = new ShippingOptionDTO() { KenticoShippingOptionID = deliveryMethod.Id, CarrierCode = deliveryMethod.SAPName, ShippingCompany = deliveryMethod.CarrierCode, ShippingService = deliveryMethod.Service.Replace("#", "") }; } return(orderDto); }
public void PartiallyFilledOrdersAreTakenIntoAccount(Language language) { var actualOrdersSubmitted = new List <SubmitOrderRequest>(); var algorithm = new QCAlgorithm(); algorithm.SubscriptionManager.SetDataManager(new DataManagerStub(algorithm)); algorithm.SetPandasConverter(); var security = algorithm.AddEquity(Symbols.AAPL.Value); security.SetMarketPrice(new TradeBar { Value = 250 }); algorithm.SetFinishedWarmingUp(); var openOrderRequest = new SubmitOrderRequest(OrderType.Market, SecurityType.Equity, Symbols.AAPL, 100, 0, 0, DateTime.MinValue, ""); openOrderRequest.SetOrderId(1); var order = Order.CreateOrder(openOrderRequest); var openOrderTicket = new OrderTicket(algorithm.Transactions, openOrderRequest); openOrderTicket.SetOrder(order); openOrderTicket.AddOrderEvent(new OrderEvent(1, Symbols.AAPL, DateTime.MinValue, OrderStatus.PartiallyFilled, OrderDirection.Buy, 250, 70, OrderFee.Zero)); var orderProcessor = new Mock <IOrderProcessor>(); orderProcessor.Setup(m => m.Process(It.IsAny <SubmitOrderRequest>())) .Returns((SubmitOrderRequest request) => new OrderTicket(algorithm.Transactions, request)) .Callback((OrderRequest request) => actualOrdersSubmitted.Add((SubmitOrderRequest)request)); orderProcessor.Setup(m => m.GetOpenOrders(It.IsAny <Func <Order, bool> >())) .Returns(new List <Order> { new MarketOrder(Symbols.AAPL, 100, DateTime.MinValue) }); orderProcessor.Setup(m => m.GetOpenOrderTickets(It.IsAny <Func <OrderTicket, bool> >())) .Returns(new List <OrderTicket> { openOrderTicket }); algorithm.Transactions.SetOrderProcessor(orderProcessor.Object); var model = GetExecutionModel(language); algorithm.SetExecution(model); var changes = SecurityChangesTests.CreateNonInternal(Enumerable.Empty <Security>(), Enumerable.Empty <Security>()); model.OnSecuritiesChanged(algorithm, changes); var targets = new IPortfolioTarget[] { new PortfolioTarget(Symbols.AAPL, 80) }; model.Execute(algorithm, targets); Assert.AreEqual(1, actualOrdersSubmitted.Count); // Remaining quantity for partially filled order = 100 - 70 = 30 // Quantity submitted = 80 - 30 = 50 Assert.AreEqual(50, actualOrdersSubmitted.Sum(x => x.Quantity)); }
private int InsertOrderRecord(SubmitOrderRequest request) { using (var cmd = new SqlCommand("usp_InsertOrder", cn, trans)) { var parameters = new [] { new SqlParameter("@OrderNumber", SqlDbType.NVarChar, 255) {Value = request.OrderNumber}, new SqlParameter("@Account", SqlDbType.NVarChar, 16) {Value = request.Account}, new SqlParameter("@FirstName", SqlDbType.NVarChar, 64) {Value = request.FirstName}, new SqlParameter("@LastName", SqlDbType.NVarChar, 64) {Value = request.LastName}, new SqlParameter("@AddressLine1", SqlDbType.NVarChar, 255) {Value = request.Address.Line1}, new SqlParameter("@AddressLine2", SqlDbType.NVarChar, 255) {Value = request.Address.Line2}, new SqlParameter("@City", SqlDbType.NVarChar, 64) {Value = request.Address.City}, new SqlParameter("@ST", SqlDbType.NVarChar, 2) {Value = request.Address.State}, new SqlParameter("@ZipCode", SqlDbType.NVarChar, 16) {Value = request.Address.Zip}, new SqlParameter("@Phone", SqlDbType.NVarChar, 16) {Value = request.Phone} }; var prmId = new SqlParameter("@ID", SqlDbType.Int) {Direction = ParameterDirection.Output}; cmd.CommandType = CommandType.StoredProcedure; cmd.Parameters.AddRange(parameters); cmd.Parameters.Add(prmId); cmd.ExecuteNonQuery(); id = Convert.ToInt32(prmId.Value); } return id; }
public async Task <SubmitOrderResponse> SubmitOrder(SubmitOrderRequest request) { return(await GetResult <SubmitOrderResponse, SubmitOrderRequest>(PrivateApiCall.SubmitOrder, request)); }
/// <summary> /// Perform preorder checks to ensure we have sufficient capital, /// the market is open, and we haven't exceeded maximum realistic orders per day. /// </summary> /// <returns>OrderResponse. If no error, order request is submitted.</returns> private OrderResponse PreOrderChecksImpl(SubmitOrderRequest request) { //Ordering 0 is useless. if (request.Quantity == 0 || request.Symbol == null || request.Symbol == QuantConnect.Symbol.Empty) { return(OrderResponse.ZeroQuantity(request)); } //If we're not tracking this symbol: throw error: if (!Securities.ContainsKey(request.Symbol) && !_sentNoDataError) { _sentNoDataError = true; return(OrderResponse.Error(request, OrderResponseErrorCode.MissingSecurity, "You haven't requested " + request.Symbol.ToString() + " data. Add this with AddSecurity() in the Initialize() Method.")); } //Set a temporary price for validating order for market orders: var security = Securities[request.Symbol]; var price = security.Price; //Check the exchange is open before sending a market on close orders if (request.OrderType == OrderType.MarketOnClose && !security.Exchange.ExchangeOpen) { return(OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.")); } if (price == 0) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityPriceZero, request.Symbol.ToString() + ": asset price is $0. If using custom data make sure you've set the 'Value' property.")); } // check quote currency existence/conversion rate on all orders Cash quoteCash; var quoteCurrency = security.QuoteCurrency.Symbol; if (!Portfolio.CashBook.TryGetValue(quoteCurrency, out quoteCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.QuoteCurrencyRequired, request.Symbol.Value + ": requires " + quoteCurrency + " in the cashbook to trade.")); } if (security.QuoteCurrency.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ConversionRateZero, request.Symbol.Value + ": requires " + quoteCurrency + " to have a non-zero conversion rate. This can be caused by lack of data.")); } // need to also check base currency existence/conversion rate on forex orders if (security.Type == SecurityType.Forex) { Cash baseCash; var baseCurrency = ((Forex)security).BaseCurrencySymbol; if (!Portfolio.CashBook.TryGetValue(baseCurrency, out baseCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " in the cashbook to trade.")); } if (baseCash.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " to have non-zero conversion rates. This can be caused by lack of data.")); } } //Make sure the security has some data: if (!security.HasData) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData, "There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point.")); } //We've already processed too many orders: max 100 per day or the memory usage explodes if (Transactions.OrdersCount > _maxOrders) { Status = AlgorithmStatus.Stopped; return(OrderResponse.Error(request, OrderResponseErrorCode.ExceededMaximumOrders, string.Format("You have exceeded maximum number of orders ({0}), for unlimited orders upgrade your account.", _maxOrders))); } if (request.OrderType == OrderType.MarketOnClose) { var nextMarketClose = security.Exchange.Hours.GetNextMarketClose(security.LocalTime, false); // must be submitted with at least 10 minutes in trading day, add buffer allow order submission var latestSubmissionTime = nextMarketClose.AddMinutes(-10.75); if (!security.Exchange.ExchangeOpen || Time > latestSubmissionTime) { // tell the user we require an 11 minute buffer, on minute data in live a user will receive the 3:49->3:50 bar at 3:50, // this is already too late to submit one of these orders, so make the user do it at the 3:48->3:49 bar so it's submitted // to the brokerage before 3:50. return(OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate, "MarketOnClose orders must be placed with at least a 11 minute buffer before market close.")); } } // passes all initial order checks return(OrderResponse.Success(request)); }
/// <summary> /// Add an order to collection and return the unique order id or negative if an error. /// </summary> /// <param name="request">A request detailing the order to be submitted</param> /// <returns>New unique, increasing orderid</returns> public OrderTicket AddOrder(SubmitOrderRequest request) { return(ProcessRequest(request)); }
public void ComputeMarginProperlyAsSecurityPriceFluctuates() { const decimal leverage = 1m; const int quantity = (int)(1000 * leverage); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var orderProcessor = new OrderProcessor(); transactions.SetOrderProcessor(orderProcessor); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].SetAmount(quantity); var config = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.AAPL]; security.SetLeverage(leverage); var time = DateTime.Now; const decimal buyPrice = 1m; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, buyPrice, buyPrice, buyPrice, buyPrice, 1)); var order = new MarketOrder(Symbols.AAPL, quantity, time) { Price = buyPrice }; var fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; orderProcessor.AddOrder(order); var request = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, order.Quantity, 0, 0, order.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); Assert.AreEqual(portfolio.CashBook["USD"].Amount, fill.FillPrice * fill.FillQuantity); portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var newOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) { Price = buyPrice }; bool sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); // now the stock doubles, so we should have margin remaining time = time.AddDays(1); const decimal highPrice = buyPrice * 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, highPrice, highPrice, highPrice, highPrice, 1)); Assert.AreEqual(quantity, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity * 2, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var anotherOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, anotherOrder); Assert.IsTrue(sufficientCapital); // now the stock plummets, so we should have negative margin remaining time = time.AddDays(1); const decimal lowPrice = buyPrice / 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, lowPrice, lowPrice, lowPrice, lowPrice, 1)); Assert.AreEqual(-quantity / 2m, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity / 2m, portfolio.TotalPortfolioValue); // this would not cause a margin call due to leverage = 1 bool issueMarginCallWarning; var marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.IsFalse(issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); // now change the leverage to test margin call warning and margin call logic security.SetLeverage(leverage * 2); // Stock price increase by minimum variation const decimal newPrice = lowPrice + 0.01m; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, newPrice, newPrice, newPrice, newPrice, 1)); // this would not cause a margin call, only a margin call warning marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.IsTrue(issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); // Price drops again to previous low, margin call orders will be issued security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, lowPrice, lowPrice, lowPrice, lowPrice, 1)); order = new MarketOrder(Symbols.AAPL, quantity, time) { Price = buyPrice }; fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.TotalPortfolioValue); marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.IsTrue(issueMarginCallWarning); Assert.AreNotEqual(0, marginCallOrders.Count); Assert.AreEqual(-security.Holdings.Quantity, marginCallOrders[0].Quantity); // we bought twice Assert.GreaterOrEqual(-portfolio.MarginRemaining, security.Price * marginCallOrders[0].Quantity); }
/// <summary> /// Perform preorder checks to ensure we have sufficient capital, /// the market is open, and we haven't exceeded maximum realistic orders per day. /// </summary> /// <returns>OrderResponse. If no error, order request is submitted.</returns> private OrderResponse PreOrderChecksImpl(SubmitOrderRequest request) { //Ordering 0 is useless. if (request.Quantity == 0 || request.Symbol == null || request.Symbol == Symbol.Empty) { return(OrderResponse.ZeroQuantity(request)); } //If we're not tracking this symbol: throw error: if (!Securities.ContainsKey(request.Symbol) && !_sentNoDataError) { _sentNoDataError = true; return(OrderResponse.Error(request, OrderResponseErrorCode.MissingSecurity, "You haven't requested " + request.Symbol.SID + " data. Add this with AddSecurity() in the Initialize() Method.")); } //Set a temporary price for validating order for market orders: var security = Securities[request.Symbol]; var price = security.Price; //Check the exchange is open before sending a market on close orders //Allow market orders, they'll just execute when the exchange reopens if (request.OrderType == OrderType.MarketOnClose && !security.Exchange.ExchangeOpen) { return(OrderResponse.Error(request, OrderResponseErrorCode.ExchangeNotOpen, request.OrderType + " order and exchange not open.")); } if (price == 0) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityPriceZero, request.Symbol.SID + ": asset price is $0. If using custom data make sure you've set the 'Value' property.")); } if (security.Type == SecurityType.Forex) { // for forex pairs we need to verify that the conversions to USD have values as well string baseCurrency, quoteCurrency; Forex.DecomposeCurrencyPair(security.Symbol.Value, out baseCurrency, out quoteCurrency); // verify they're in the portfolio Cash baseCash, quoteCash; if (!Portfolio.CashBook.TryGetValue(baseCurrency, out baseCash) || !Portfolio.CashBook.TryGetValue(quoteCurrency, out quoteCash)) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexBaseAndQuoteCurrenciesRequired, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " in the cashbook to trade.")); } // verify we have conversion rates for each leg of the pair back into the account currency if (baseCash.ConversionRate == 0m || quoteCash.ConversionRate == 0m) { return(OrderResponse.Error(request, OrderResponseErrorCode.ForexConversionRateZero, request.Symbol.Value + ": requires " + baseCurrency + " and " + quoteCurrency + " to have non-zero conversion rates. This can be caused by lack of data.")); } } //Make sure the security has some data: if (!security.HasData) { return(OrderResponse.Error(request, OrderResponseErrorCode.SecurityHasNoData, "There is no data for this symbol yet, please check the security.HasData flag to ensure there is at least one data point.")); } //We've already processed too many orders: max 100 per day or the memory usage explodes if (Transactions.OrdersCount > _maxOrders) { _quit = true; return(OrderResponse.Error(request, OrderResponseErrorCode.ExceededMaximumOrders, string.Format("You have exceeded maximum number of orders ({0}), for unlimited orders upgrade your account.", _maxOrders))); } if (request.OrderType == OrderType.MarketOnClose) { // must be submitted with at least 10 minutes in trading day, add buffer allow order submission var latestSubmissionTime = (Time.Date + security.Exchange.MarketClose).AddMinutes(-10.75); if (Time > latestSubmissionTime) { // tell the user we require an 11 minute buffer, on minute data in live a user will receive the 3:49->3:50 bar at 3:50, // this is already too late to submit one of these orders, so make the user do it at the 3:48->3:49 bar so it's submitted // to the brokerage before 3:50. return(OrderResponse.Error(request, OrderResponseErrorCode.MarketOnCloseOrderTooLate, "MarketOnClose orders must be placed with at least a 11 minute buffer before market close.")); } } // passes all initial order checks return(OrderResponse.Success(request)); }
public void MarginComputesProperlyWithMultipleSecurities() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var orderProcessor = new OrderProcessor(); transactions.SetOrderProcessor(orderProcessor); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].SetAmount(1000); portfolio.CashBook.Add("EUR", 1000, 1.1m); portfolio.CashBook.Add("GBP", -1000, 2.0m); var eurCash = portfolio.CashBook["EUR"]; var gbpCash = portfolio.CashBook["GBP"]; var usdCash = portfolio.CashBook["USD"]; var time = DateTime.Now; var config1 = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config1, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); securities[Symbols.AAPL].SetLeverage(2m); securities[Symbols.AAPL].Holdings.SetHoldings(100, 100); securities[Symbols.AAPL].SetMarketPrice(new TradeBar { Time = time, Value = 100 }); //Console.WriteLine("AAPL TMU: " + securities[Symbols.AAPL].MarginModel.GetMaintenanceMargin(securities[Symbols.AAPL])); //Console.WriteLine("AAPL Value: " + securities[Symbols.AAPL].Holdings.HoldingsValue); //Console.WriteLine(); var config2 = CreateTradeBarDataConfig(SecurityType.Forex, Symbols.EURUSD); securities.Add(new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, usdCash, config2, SymbolProperties.GetDefault(CashBook.AccountCurrency))); securities[Symbols.EURUSD].SetLeverage(100m); securities[Symbols.EURUSD].Holdings.SetHoldings(1.1m, 1000); securities[Symbols.EURUSD].SetMarketPrice(new TradeBar { Time = time, Value = 1.1m }); //Console.WriteLine("EURUSD TMU: " + securities[Symbols.EURUSD].MarginModel.GetMaintenanceMargin(securities[Symbols.EURUSD])); //Console.WriteLine("EURUSD Value: " + securities[Symbols.EURUSD].Holdings.HoldingsValue); //Console.WriteLine(); var config3 = CreateTradeBarDataConfig(SecurityType.Forex, Symbols.EURGBP); securities.Add(new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, gbpCash, config3, SymbolProperties.GetDefault(gbpCash.Symbol))); securities[Symbols.EURGBP].SetLeverage(100m); securities[Symbols.EURGBP].Holdings.SetHoldings(1m, 1000); securities[Symbols.EURGBP].SetMarketPrice(new TradeBar { Time = time, Value = 1m }); //Console.WriteLine("EURGBP TMU: " + securities[Symbols.EURGBP].MarginModel.GetMaintenanceMargin(securities[Symbols.EURGBP])); //Console.WriteLine("EURGBP Value: " + securities[Symbols.EURGBP].Holdings.HoldingsValue); //Console.WriteLine(); //Console.WriteLine(portfolio.CashBook["USD"]); //Console.WriteLine(portfolio.CashBook["EUR"]); //Console.WriteLine(portfolio.CashBook["GBP"]); //Console.WriteLine("CashBook: " + portfolio.CashBook.TotalValueInAccountCurrency); //Console.WriteLine(); //Console.WriteLine("Total Margin Used: " + portfolio.TotalMarginUsed); //Console.WriteLine("Total Free Margin: " + portfolio.MarginRemaining); //Console.WriteLine("Total Portfolio Value: " + portfolio.TotalPortfolioValue); var acceptedOrder = new MarketOrder(Symbols.AAPL, 101, DateTime.Now) { Price = 100 }; orderProcessor.AddOrder(acceptedOrder); var request = new SubmitOrderRequest(OrderType.Market, acceptedOrder.SecurityType, acceptedOrder.Symbol, acceptedOrder.Quantity, 0, 0, acceptedOrder.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); var sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, acceptedOrder); Assert.IsTrue(sufficientCapital); var rejectedOrder = new MarketOrder(Symbols.AAPL, 102, DateTime.Now) { Price = 100 }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, rejectedOrder); Assert.IsFalse(sufficientCapital); }
/// <summary> /// Handles a request to submit a new order /// </summary> private OrderResponse HandleSubmitOrderRequest(SubmitOrderRequest request) { OrderTicket ticket; var order = Order.CreateOrder(request); if (!_orders.TryAdd(order.Id, order)) { Log.Error("BrokerageTransactionHandler.HandleSubmitOrderRequest(): Unable to add new order, order not processed."); return(OrderResponse.Error(request, OrderResponseErrorCode.OrderAlreadyExists, "Cannot process submit request because order with id {0} already exists")); } if (!_orderTickets.TryGetValue(order.Id, out ticket)) { Log.Error("BrokerageTransactionHandler.HandleSubmitOrderRequest(): Unable to retrieve order ticket, order not processed."); return(OrderResponse.UnableToFindOrder(request)); } // update the ticket's internal storage with this new order reference ticket.SetOrder(order); // check to see if we have enough money to place the order bool sufficientCapitalForOrder; try { sufficientCapitalForOrder = _algorithm.Transactions.GetSufficientCapitalForOrder(_algorithm.Portfolio, order); } catch (Exception err) { Log.Error(err); _algorithm.Error(string.Format("Order Error: id: {0}, Error executing margin models: {1}", order.Id, err.Message)); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "Error executing margin models")); return(OrderResponse.Error(request, OrderResponseErrorCode.ProcessingError, "Error in GetSufficientCapitalForOrder")); } if (!sufficientCapitalForOrder) { order.Status = OrderStatus.Invalid; var security = _algorithm.Securities[order.Symbol]; var response = OrderResponse.Error(request, OrderResponseErrorCode.InsufficientBuyingPower, string.Format("Order Error: id: {0}, Insufficient buying power to complete order (Value:{1}).", order.Id, order.GetValue(security).SmartRounding())); _algorithm.Error(response.ErrorMessage); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "Insufficient buying power to complete order")); return(response); } // verify that our current brokerage can actually take the order BrokerageMessageEvent message; if (!_algorithm.LiveMode && !_algorithm.BrokerageModel.CanSubmitOrder(_algorithm.Securities[order.Symbol], order, out message)) { // if we couldn't actually process the order, mark it as invalid and bail order.Status = OrderStatus.Invalid; if (message == null) { message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "InvalidOrder", "BrokerageModel declared unable to submit order: " + order.Id); } var response = OrderResponse.Error(request, OrderResponseErrorCode.BrokerageModelRefusedToSubmitOrder, "OrderID: " + order.Id + " " + message); _algorithm.Error(response.ErrorMessage); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "BrokerageModel declared unable to submit order")); return(response); } // set the order status based on whether or not we successfully submitted the order to the market bool orderPlaced; try { orderPlaced = _brokerage.PlaceOrder(order); } catch (Exception err) { Log.Error(err); orderPlaced = false; } if (!orderPlaced) { // we failed to submit the order, invalidate it order.Status = OrderStatus.Invalid; var errorMessage = "Brokerage failed to place order: " + order.Id; var response = OrderResponse.Error(request, OrderResponseErrorCode.BrokerageFailedToSubmitOrder, errorMessage); _algorithm.Error(response.ErrorMessage); HandleOrderEvent(new OrderEvent(order, _algorithm.UtcTime, 0m, "Brokerage failed to place order")); return(response); } order.Status = OrderStatus.Submitted; return(OrderResponse.Success(request)); }
public void ComputeMarginProperlyShortCoverZeroLong() { const decimal leverage = 2m; const int amount = 1000; const int quantity = (int)(amount * leverage); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var orderProcessor = new OrderProcessor(); transactions.SetOrderProcessor(orderProcessor); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].SetAmount(amount); var config = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.AAPL]; security.SetLeverage(leverage); var time = DateTime.Now; const decimal sellPrice = 1m; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, sellPrice, sellPrice, sellPrice, sellPrice, 1)); var order = new MarketOrder(Symbols.AAPL, -quantity, time) { Price = sellPrice }; var fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = sellPrice, FillQuantity = -quantity }; orderProcessor.AddOrder(order); var request = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, order.Quantity, 0, 0, order.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); portfolio.ProcessFill(fill); // we shouldn't be able to place a new short order var newOrder = new MarketOrder(Symbols.AAPL, -1, time.AddSeconds(1)) { Price = sellPrice }; var sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); // we should be able to place cover to zero newOrder = new MarketOrder(Symbols.AAPL, quantity, time.AddSeconds(1)) { Price = sellPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsTrue(sufficientCapital); // now the stock doubles, so we should have negative margin remaining time = time.AddDays(1); const decimal highPrice = sellPrice * 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, highPrice, highPrice, highPrice, highPrice, 1)); // we still shouldn be able to place cover to zero newOrder = new MarketOrder(Symbols.AAPL, quantity, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsTrue(sufficientCapital); // we shouldn't be able to place cover to long newOrder = new MarketOrder(Symbols.AAPL, quantity + 1, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); }
public void GenerateMarginCallOrderTests() { const int quantity = 1000; const decimal leverage = 1m; var orderProcessor = new FakeOrderProcessor(); var portfolio = GetPortfolio(orderProcessor, quantity); portfolio.MarginCallModel = new DefaultMarginCallModel(portfolio, null); var security = GetSecurity(Symbols.AAPL); portfolio.Securities.Add(security); var time = DateTime.Now; const decimal buyPrice = 1m; security.SetMarketPrice(new Tick(time, Symbols.AAPL, buyPrice, buyPrice)); var order = new MarketOrder(Symbols.AAPL, quantity, time) { Price = buyPrice }; var fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; orderProcessor.AddOrder(order); var request = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, order.Quantity, 0, 0, order.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); Assert.AreEqual(portfolio.Cash, fill.FillPrice * fill.FillQuantity); portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var newOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) { Price = buyPrice }; var hasSufficientBuyingPower = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(portfolio, security, newOrder).IsSufficient; Assert.IsFalse(hasSufficientBuyingPower); // now the stock doubles, so we should have margin remaining time = time.AddDays(1); const decimal highPrice = buyPrice * 2; security.SetMarketPrice(new Tick(time, Symbols.AAPL, highPrice, highPrice)); Assert.AreEqual(quantity, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity * 2, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var anotherOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) { Price = highPrice }; hasSufficientBuyingPower = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(portfolio, security, anotherOrder).IsSufficient; Assert.IsTrue(hasSufficientBuyingPower); // now the stock plummets, so we should have negative margin remaining time = time.AddDays(1); const decimal lowPrice = buyPrice / 2; security.SetMarketPrice(new Tick(time, Symbols.AAPL, lowPrice, lowPrice)); Assert.AreEqual(-quantity / 2m, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity / 2m, portfolio.TotalPortfolioValue); // this would not cause a margin call due to leverage = 1 bool issueMarginCallWarning; var marginCallOrders = portfolio.MarginCallModel.GetMarginCallOrders(out issueMarginCallWarning); Assert.IsFalse(issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); // now change the leverage to test margin call warning and margin call logic security.SetLeverage(leverage * 2); // Stock price increase by minimum variation const decimal newPrice = lowPrice + 0.01m; security.SetMarketPrice(new Tick(time, Symbols.AAPL, newPrice, newPrice)); // this would not cause a margin call, only a margin call warning marginCallOrders = portfolio.MarginCallModel.GetMarginCallOrders(out issueMarginCallWarning); Assert.IsTrue(issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); // Price drops again to previous low, margin call orders will be issued security.SetMarketPrice(new Tick(time, Symbols.AAPL, lowPrice, lowPrice)); order = new MarketOrder(Symbols.AAPL, quantity, time) { Price = buyPrice }; fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.TotalPortfolioValue); marginCallOrders = portfolio.MarginCallModel.GetMarginCallOrders(out issueMarginCallWarning); Assert.IsTrue(issueMarginCallWarning); Assert.AreEqual(1, marginCallOrders.Count); }
public Task <IObjectClientResponse <SubmitOrderResponse> > SubmitOrderAsync(SubmitOrderRequest request) { var id = request.Id; return(Client.PostAsync <SubmitOrderResponse>($"{id}/submit")); }