public void OrderByMarginImpactDoesNotReturnTargetsForWhichUnorderedQuantityIsZeroBecauseOpenOrder() { var algorithm = new FakeAlgorithm(); var orderProcessor = new FakeOrderProcessor(); algorithm.Transactions.SetOrderProcessor(orderProcessor); var symbol = new Symbol(SecurityIdentifier.GenerateEquity(_symbol, Market.USA), _symbol); var equity = algorithm.AddEquity(symbol); equity.Cache.AddData(new TradeBar(DateTime.UtcNow, symbol, 1, 1, 1, 1, 1)); var collection = new PortfolioTargetCollection(); var target = new PortfolioTarget(symbol, 1); collection.Add(target); var openOrderRequest = new SubmitOrderRequest(OrderType.Market, symbol.SecurityType, symbol, 1, 0, 0, DateTime.UtcNow, ""); openOrderRequest.SetOrderId(1); var openOrderTicket = new OrderTicket(algorithm.Transactions, openOrderRequest); orderProcessor.AddOrder(new MarketOrder(symbol, 1, DateTime.UtcNow)); orderProcessor.AddTicket(openOrderTicket); var targets = collection.OrderByMarginImpact(algorithm); Assert.AreEqual(collection.Count, 1); Assert.IsTrue(targets.IsNullOrEmpty()); }
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 openOrderTicket = new OrderTicket(algorithm.Transactions, openOrderRequest); 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((SubmitOrderRequest request) => actualOrdersSubmitted.Add(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 = new SecurityChanges(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)); }
/// <summary> /// Turn order into an order ticket /// </summary> /// <param name="order">The <see cref="Order"/> being converted</param> /// <param name="transactionManager">The transaction manager, <see cref="SecurityTransactionManager"/></param> /// <returns></returns> public static OrderTicket ToOrderTicket(this Order order, SecurityTransactionManager transactionManager) { var limitPrice = 0m; var stopPrice = 0m; switch (order.Type) { case OrderType.Limit: var limitOrder = order as LimitOrder; limitPrice = limitOrder.LimitPrice; break; case OrderType.StopMarket: var stopMarketOrder = order as StopMarketOrder; stopPrice = stopMarketOrder.StopPrice; break; case OrderType.StopLimit: var stopLimitOrder = order as StopLimitOrder; stopPrice = stopLimitOrder.StopPrice; limitPrice = stopLimitOrder.LimitPrice; break; case OrderType.OptionExercise: case OrderType.Market: case OrderType.MarketOnOpen: case OrderType.MarketOnClose: limitPrice = order.Price; stopPrice = order.Price; break; default: throw new ArgumentOutOfRangeException(); } var submitOrderRequest = new SubmitOrderRequest(order.Type, order.SecurityType, order.Symbol, order.Quantity, stopPrice, limitPrice, order.Time, order.Tag, order.Properties); submitOrderRequest.SetOrderId(order.Id); return(new OrderTicket(transactionManager, submitOrderRequest)); }
public void TestInvalidWarmingUp() { var orderRequest = new SubmitOrderRequest(OrderType.Limit, SecurityType.Equity, Symbols.AAPL, 1000, 0, 1.11m, DateTime.Now, "Pepe"); orderRequest.SetOrderId(orderRequest.OrderId); var ticket = OrderTicket.InvalidWarmingUp(null, orderRequest); Assert.AreEqual(ticket.OrderId, orderRequest.OrderId); Assert.AreEqual(ticket.Quantity, 1000); Assert.AreEqual(ticket.Tag, "Pepe"); Assert.AreEqual(ticket.Status, OrderStatus.Invalid); Assert.AreEqual(ticket.OrderType, OrderType.Limit); Assert.AreEqual(ticket.SecurityType, SecurityType.Equity); Assert.AreEqual(ticket.Symbol, Symbols.AAPL); Assert.AreEqual(ticket.SubmitRequest, orderRequest); Assert.AreEqual(ticket.SubmitRequest.Status, OrderRequestStatus.Error); Assert.AreEqual(ticket.SubmitRequest.OrderId, orderRequest.OrderId); Assert.AreEqual(ticket.SubmitRequest.Quantity, 1000); Assert.AreEqual(ticket.SubmitRequest.Tag, "Pepe"); }
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 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); }
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); } }
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 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); }
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); }