/// <summary> /// Returned an ordered enumerable where position reducing orders are executed first /// and the remaining orders are executed in decreasing order value. /// Will NOT return targets for securities that have no data yet. /// Will NOT return targets for which current holdings + open orders quantity, sum up to the target quantity /// </summary> /// <param name="algorithm">The algorithm instance</param> public IEnumerable <IPortfolioTarget> OrderByMarginImpact(IAlgorithm algorithm) { if (Count == 0) { // shortcut for performance return(Enumerable.Empty <IPortfolioTarget>()); } return(_targets .Select(x => x.Value) .Where(x => { var security = algorithm.Securities[x.Symbol]; return security.HasData && Math.Abs(OrderSizing.GetUnorderedQuantity(algorithm, x)) >= security.SymbolProperties.LotSize; }) .Select(x => new { PortfolioTarget = x, TargetQuantity = x.Quantity, ExistingQuantity = algorithm.Portfolio[x.Symbol].Quantity + algorithm.Transactions.GetOpenOrderTickets(x.Symbol) .Aggregate(0m, (d, t) => d + t.Quantity - t.QuantityFilled), Price = algorithm.Securities[x.Symbol].Price }) .Select(x => new { PortfolioTarget = x.PortfolioTarget, OrderValue = Math.Abs((x.TargetQuantity - x.ExistingQuantity) * x.Price), IsReducingPosition = x.ExistingQuantity != 0 && Math.Abs(x.TargetQuantity) < Math.Abs(x.ExistingQuantity) }) .OrderByDescending(x => x.IsReducingPosition) .ThenByDescending(x => x.OrderValue) .Select(x => x.PortfolioTarget)); }
public override void Execute(QCAlgorithm algorithm, IPortfolioTarget[] targets) { var orders = targets.GroupBy(t => t.Symbol) .Select( g => new { Symbol = g.Key, Quantity = g.Sum(x => OrderSizing.GetUnorderedQuantity(algorithm, x)) } ) .Where(order => algorithm.Securities[order.Symbol].HasData && order.Quantity != 0); foreach (var order in orders) { var tickets = DoExecute(algorithm, order.Symbol, order.Quantity); foreach (var ticket in tickets) { ticket.OrderClosed .WaitOneAsync() .ContinueWith( task => { if (task.Result) { algorithm.Debug($"{algorithm.Time} - ({ticket.OrderId}) {ticket.QuantityFilled} share of {ticket.Symbol} is filled @{ticket.AverageFillPrice}."); } } ); } } }
public void AdjustByLotSize(decimal quantity, decimal expected) { var algo = new AlgorithmStub(); var security = algo.AddEquity(Symbols.SPY.Value); var result = OrderSizing.AdjustByLotSize(security, quantity); Assert.AreEqual(expected, result); }
public void GetOrderSizeForPercentVolume() { var algo = new AlgorithmStub(); var security = algo.AddFutureContract(Symbols.Future_CLF19_Jan2019); security.SetMarketPrice(new TradeBar { Value = 250, Volume = 10 }); var result = OrderSizing.GetOrderSizeForPercentVolume(security, 0.5m, 100); Assert.AreEqual(5, result); }
public void GetUnorderedQuantityHoldingsNoOrders(decimal holdings, decimal target, decimal expected) { var algo = new AlgorithmStub(); algo.Transactions.SetOrderProcessor(new FakeOrderProcessor()); var security = algo.AddFutureContract(Symbols.Future_CLF19_Jan2019); security.SetMarketPrice(new TradeBar { Value = 250 }); security.Holdings.SetHoldings(250, holdings); var result = OrderSizing.GetUnorderedQuantity(algo, new PortfolioTarget(Symbols.Future_CLF19_Jan2019, target)); Assert.AreEqual(expected, result); }
public void GetOrderSizeForMaximumValue(decimal maximumOrderValue, decimal target, decimal expected) { var algo = new AlgorithmStub(); var security = algo.AddFutureContract(Symbols.Future_CLF19_Jan2019); security.SetMarketPrice(new TradeBar { Value = 250 }); var result = OrderSizing.GetOrderSizeForMaximumValue(security, maximumOrderValue, target); var expectedCalculated = maximumOrderValue / (security.Price * security.SymbolProperties.ContractMultiplier); expectedCalculated -= expectedCalculated % security.SymbolProperties.LotSize; Assert.AreEqual(Math.Min(expectedCalculated, Math.Abs(target)) * Math.Sign(target), result); Assert.AreEqual(expected, result); }
public void GetUnorderedQuantityHoldingsOpenOrders(decimal holdings, decimal target, decimal filledQuantity, decimal expected) { var algo = new AlgorithmStub(); var orderProcessor = new FakeOrderProcessor(); var orderRequest = new SubmitOrderRequest( OrderType.Market, SecurityType.Future, Symbols.Future_CLF19_Jan2019, filledQuantity * 2, 250, 250, new DateTime(2020, 1, 1), "Pepe" ); var order = Order.CreateOrder(orderRequest); var ticket = new OrderTicket(algo.Transactions, orderRequest); ticket.SetOrder(order); ticket.AddOrderEvent(new OrderEvent(1, Symbols.Future_CLF19_Jan2019, new DateTime(2020, 1, 1), OrderStatus.Filled, filledQuantity > 0 ? OrderDirection.Buy : OrderDirection.Sell, 250, filledQuantity, OrderFee.Zero)); orderProcessor.AddTicket(ticket); algo.Transactions.SetOrderProcessor(orderProcessor); var security = algo.AddFutureContract(Symbols.Future_CLF19_Jan2019); security.SetMarketPrice(new TradeBar { Value = 250 }); security.Holdings.SetHoldings(250, holdings); var result = OrderSizing.GetUnorderedQuantity(algo, new PortfolioTarget(Symbols.Future_CLF19_Jan2019, target)); Assert.AreEqual(expected, result); }