public void OrderQuantityIsCeiledToNearestMultipleOfLotSizeWhenShortOrderIsRounded() { //Initializes the transaction handler var transactionHandler = new BrokerageTransactionHandler(); transactionHandler.Initialize(_algorithm, new BacktestingBrokerage(_algorithm), new BacktestingResultHandler()); // Creates the order var security = _algorithm.Securities[Ticker]; var orderRequest = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, -1600, 0, 0, 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); // Act var orderTicket = transactionHandler.Process(orderRequest); Assert.IsTrue(orderTicket.Status == OrderStatus.New); transactionHandler.HandleOrderRequest(orderRequest); // Assert Assert.IsTrue(orderRequest.Response.IsProcessed); Assert.IsTrue(orderRequest.Response.IsSuccess); Assert.IsTrue(orderTicket.Status == OrderStatus.Submitted); // -1600 after round off becomes -1000 Assert.AreEqual(-1000, orderTicket.Quantity); }
/// <summary> /// Runs this command against the specified algorithm instance /// </summary> /// <param name="algorithm">The algorithm to run this command against</param> public CommandResultPacket Run(IAlgorithm algorithm) { var request = new SubmitOrderRequest(OrderType, SecurityType, Symbol, Quantity, StopPrice, LimitPrice, DateTime.UtcNow, Tag); var ticket = algorithm.Transactions.ProcessRequest(request); var response = ticket.GetMostRecentOrderResponse(); var message = string.Format("{0} for {1} units of {2}: {3}", OrderType, Quantity, Symbol, response); if (response.IsSuccess) { algorithm.Debug(message); } else { algorithm.Error(message); } return new CommandResultPacket(this, response.IsSuccess); }
/// <summary> /// Creates a new <see cref="OrderTicket"/> that is invalidated because the algorithm was in the middle of warm up still /// </summary> public static OrderTicket InvalidWarmingUp(SecurityTransactionManager transactionManager, SubmitOrderRequest submit) { submit.SetResponse(OrderResponse.WarmingUp(submit)); var ticket = new OrderTicket(transactionManager, submit); ticket._orderStatusOverride = OrderStatus.Invalid; return(ticket); }
/// <summary> /// Creates a new <see cref="OrderTicket"/> that represents trying to submit a new order that had errors embodied in the <paramref name="response"/> /// </summary> public static OrderTicket InvalidSubmitRequest(SecurityTransactionManager transactionManager, SubmitOrderRequest request, OrderResponse response) { request.SetResponse(response); return(new OrderTicket(transactionManager, request) { _orderStatusOverride = OrderStatus.Invalid }); }
private static DateTime InitializeTest(out BasicTemplateAlgorithm algorithm, out Security security, out PartialMarketFillModel model, out MarketOrder order, out OrderTicket ticket) { var referenceTimeNY = new DateTime(2015, 12, 21, 13, 0, 0); var referenceTimeUtc = referenceTimeNY.ConvertToUtc(TimeZones.NewYork); algorithm = new BasicTemplateAlgorithm(); algorithm.SetDateTime(referenceTimeUtc); var transactionHandler = new BacktestingTransactionHandler(); transactionHandler.Initialize(algorithm, new BacktestingBrokerage(algorithm), new TestResultHandler(Console.WriteLine)); Task.Run(() => transactionHandler.Run()); algorithm.Transactions.SetOrderProcessor(transactionHandler); var config = new SubscriptionDataConfig(typeof(TradeBar), Symbols.SPY, Resolution.Second, TimeZones.NewYork, TimeZones.NewYork, false, false, false); security = new Security(SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork), config); model = new PartialMarketFillModel(algorithm.Transactions, 2); algorithm.Securities.Add(security); algorithm.Securities[Symbols.SPY].FillModel = model; security.SetMarketPrice(new Tick { Symbol = Symbols.SPY, Value = 100 }); algorithm.SetFinishedWarmingUp(); order = new MarketOrder(Symbols.SPY, 100, referenceTimeUtc) { Id = 1 }; var request = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, order.Quantity, 0, 0, algorithm.UtcTime, null); ticket = algorithm.Transactions.ProcessRequest(request); return referenceTimeUtc; }
public void GenerateMarginCallOrderTests() { const int quantity = 1000; const decimal leverage = 1m; var orderProcessor = new OrderProcessor(); var portfolio = GetPortfolio(orderProcessor, quantity); var security = GetSecurity(Symbols.AAPL); security.MarginModel = new NoMarginCallMarginModel(leverage); 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}; bool sufficientCapital = portfolio.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 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 }; sufficientCapital = portfolio.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 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.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 Tick(time, Symbols.AAPL, newPrice, newPrice)); // 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 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); // Even with TotalPortfolioValue == 0, do not issue warning or orders marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.IsFalse(issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); }
/// <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; 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 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(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; } order.Status = OrderStatus.Submitted; 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) { request.SetResponse(OrderResponse.Success(request), OrderRequestStatus.Processing); var ticket = new OrderTicket(_algorithm.Transactions, request); _orderTickets.TryAdd(ticket.OrderId, ticket); // send the order to be processed after creating the ticket _orderRequestQueue.Enqueue(request); return ticket; }
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 StopMarketOrderPriceIsRounded() { //Initializes the transaction handler var transactionHandler = new BrokerageTransactionHandler(); transactionHandler.Initialize(_algorithm, new BacktestingBrokerage(_algorithm), new BacktestingResultHandler()); // Creates the order var security = _algorithm.Securities[Ticker]; var price = 1.12129m; security.SetMarketPrice(new Tick(DateTime.Now, security.Symbol, price, price, price)); var orderRequest = new SubmitOrderRequest(OrderType.StopMarket, security.Type, security.Symbol, 1600, 1.12131212m, 1.12131212m, 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); // Act var orderTicket = transactionHandler.Process(orderRequest); Assert.IsTrue(orderTicket.Status == OrderStatus.New); transactionHandler.HandleOrderRequest(orderRequest); // Assert Assert.IsTrue(orderRequest.Response.IsProcessed); Assert.IsTrue(orderRequest.Response.IsSuccess); Assert.IsTrue(orderTicket.Status == OrderStatus.Submitted); // 1600 after round off becomes 1000 Assert.AreEqual(1000, orderTicket.Quantity); // 1.12131212 after round becomes 1.12131 Assert.AreEqual(1.12131m, orderTicket.Get(OrderField.StopPrice)); }
public void OrderIsNotPlacedWhenOrderIsLowerThanLotSize() { //Initializes the transaction handler var transactionHandler = new BrokerageTransactionHandler(); transactionHandler.Initialize(_algorithm, new BacktestingBrokerage(_algorithm), new BacktestingResultHandler()); // Creates the order var security = _algorithm.Securities[Ticker]; var orderRequest = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, 600, 0, 0, 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); // Act var orderTicket = transactionHandler.Process(orderRequest); Assert.IsTrue(orderTicket.Status == OrderStatus.New); transactionHandler.HandleOrderRequest(orderRequest); // 600 after round off becomes 0 -> order is not placed Assert.IsTrue(orderRequest.Response.IsProcessed); Assert.IsTrue(orderRequest.Response.IsError); Assert.IsTrue(orderTicket.Status == OrderStatus.Invalid); }
/// <summary> /// Creates an <see cref="Order"/> to match the specified <paramref name="request"/> /// </summary> /// <param name="request">The <see cref="SubmitOrderRequest"/> to create an order for</param> /// <returns>The <see cref="Order"/> that matches the request</returns> public static Order CreateOrder(SubmitOrderRequest request) { return(CreateOrder(request.OrderId, request.OrderType, request.Symbol, request.Quantity, request.Time, request.Tag, request.OrderProperties, request.LimitPrice, request.StopPrice, request.TriggerPrice)); }
/// <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); } } } }
/// <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(-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 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.AreEqual(0, marginCallOrders.Count); // now change the leverage and buy more and we'll get a margin call security.SetLeverage(leverage * 2); 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.AreNotEqual(0, marginCallOrders.Count); Assert.AreEqual(-security.Holdings.Quantity, marginCallOrders[0].Quantity); // we bought twice Assert.GreaterOrEqual(-portfolio.MarginRemaining, security.Price * marginCallOrders[0].Quantity); }
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> /// 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) { var response = !_algorithm.IsWarmingUp ? OrderResponse.Success(request) : OrderResponse.WarmingUp(request); request.SetResponse(response); var ticket = new OrderTicket(_algorithm.Transactions, request); _orderTickets.TryAdd(ticket.OrderId, ticket); // send the order to be processed after creating the ticket if (response.IsSuccess) { _orderRequestQueue.Enqueue(request); } else { // add it to the orders collection for recall later var order = Order.CreateOrder(request); order.Status = OrderStatus.Invalid; order.Tag = "Algorithm warming up."; ticket.SetOrder(order); _orders.TryAdd(request.OrderId, order); } return ticket; }
/// <summary> /// Creates an <see cref="Order"/> to match the specified <paramref name="request"/> /// </summary> /// <param name="request">The <see cref="SubmitOrderRequest"/> to create an order for</param> /// <returns>The <see cref="Order"/> that matches the request</returns> public static Order CreateOrder(SubmitOrderRequest request) { Order order; switch (request.OrderType) { case OrderType.Market: order = new MarketOrder(request.Symbol, request.Quantity, request.Time, request.Tag, request.SecurityType); break; case OrderType.Limit: order = new LimitOrder(request.Symbol, request.Quantity, request.LimitPrice, request.Time, request.Tag, request.SecurityType); break; case OrderType.StopMarket: order = new StopMarketOrder(request.Symbol, request.Quantity, request.StopPrice, request.Time, request.Tag, request.SecurityType); break; case OrderType.StopLimit: order = new StopLimitOrder(request.Symbol, request.Quantity, request.StopPrice, request.LimitPrice, request.Time, request.Tag, request.SecurityType); break; case OrderType.MarketOnOpen: order = new MarketOnOpenOrder(request.Symbol, request.SecurityType, request.Quantity, request.Time, request.Tag); break; case OrderType.MarketOnClose: order = new MarketOnCloseOrder(request.Symbol, request.SecurityType, request.Quantity, request.Time, request.Tag); break; default: throw new ArgumentOutOfRangeException(); } order.Status = OrderStatus.New; order.Id = request.OrderId; if (request.Tag != null) { order.Tag = request.Tag; } return order; }
/// <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 PreOrderChecks(SubmitOrderRequest request) { var response = PreOrderChecksImpl(request); if (response.IsError) { Error(response.ErrorMessage); } return response; }
/// <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) { var response = !_algorithm.IsWarmingUp ? OrderResponse.Success(request) : OrderResponse.WarmingUp(request); request.SetResponse(response); var ticket = new OrderTicket(_algorithm.Transactions, request); _orderTickets.TryAdd(ticket.OrderId, ticket); // send the order to be processed after creating the ticket if (response.IsSuccess) { _orderRequestQueue.Add(request); } else { // add it to the orders collection for recall later var order = Order.CreateOrder(request); // ensure the order is tagged with a currency var security = _algorithm.Securities[order.Symbol]; order.PriceCurrency = security.SymbolProperties.QuoteCurrency; order.Status = OrderStatus.Invalid; order.Tag = "Algorithm warming up."; ticket.SetOrder(order); _orders.TryAdd(request.OrderId, order); } return ticket; }
/// <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.Permtick + " 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.Permtick + ": 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) { 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) { // 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); }
/// <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); }
/// <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, 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); } } } }