/// <summary> /// Performs application of an OrderEvent to the portfolio /// </summary> /// <param name="portfolio">The algorithm's portfolio</param> /// <param name="security">The fill's security</param> /// <param name="fill">The order event fill object to be applied</param> public virtual void ProcessFill(SecurityPortfolioManager portfolio, Security security, OrderEvent fill) { var quoteCash = security.QuoteCurrency; //Get the required information from the vehicle this order will affect var isLong = security.Holdings.IsLong; var isShort = security.Holdings.IsShort; var closedPosition = false; //Make local decimals to avoid any rounding errors from int multiplication var quantityHoldings = (decimal)security.Holdings.Quantity; var absoluteHoldingsQuantity = security.Holdings.AbsoluteQuantity; var averageHoldingsPrice = security.Holdings.AveragePrice; try { // apply sales value to holdings in the account currency if (security.Type == SecurityType.Future) { // for futures, we measure volume of sales, not notionals var saleValueInQuoteCurrency = fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity); var saleValue = saleValueInQuoteCurrency * quoteCash.ConversionRate; security.Holdings.AddNewSale(saleValue); } else { var saleValueInQuoteCurrency = fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity) * security.SymbolProperties.ContractMultiplier; var saleValue = saleValueInQuoteCurrency * quoteCash.ConversionRate; security.Holdings.AddNewSale(saleValue); } // subtract transaction fees from the portfolio (assumes in account currency) var feeThisOrder = Math.Abs(fill.OrderFee); security.Holdings.AddNewFee(feeThisOrder); portfolio.CashBook[CashBook.AccountCurrency].AddAmount(-feeThisOrder); // apply the funds using the current settlement model // we dont adjust funds for futures: it is zero upfront payment derivative (margin applies though) if (security.Type != SecurityType.Future) { security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, quoteCash.Symbol, -fill.FillQuantity * fill.FillPrice * security.SymbolProperties.ContractMultiplier); } if (security.Type == SecurityType.Forex || security.Type == SecurityType.Crypto) { // model forex fills as currency swaps var forex = (IBaseCurrencySymbol)security; security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, forex.BaseCurrencySymbol, fill.FillQuantity); } // did we close or open a position further? closedPosition = isLong && fill.Direction == OrderDirection.Sell || isShort && fill.Direction == OrderDirection.Buy; // calculate the last trade profit if (closedPosition) { // profit = (closed sale value - cost)*conversion to account currency // closed sale value = quantity closed * fill price BUYs are deemed negative cash flow // cost = quantity closed * average holdings price SELLS are deemed positive cash flow var absoluteQuantityClosed = Math.Min(fill.AbsoluteFillQuantity, absoluteHoldingsQuantity); var closedSaleValueInQuoteCurrency = Math.Sign(-fill.FillQuantity) * fill.FillPrice * absoluteQuantityClosed; var closedCost = Math.Sign(-fill.FillQuantity) * absoluteQuantityClosed * averageHoldingsPrice; var lastTradeProfit = (closedSaleValueInQuoteCurrency - closedCost) * security.SymbolProperties.ContractMultiplier; var lastTradeProfitInAccountCurrency = lastTradeProfit * security.QuoteCurrency.ConversionRate; // Reflect account cash adjustment for futures position if (security.Type == SecurityType.Future) { security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, quoteCash.Symbol, lastTradeProfit); } //Update Vehicle Profit Tracking: security.Holdings.AddNewProfit(lastTradeProfitInAccountCurrency); security.Holdings.SetLastTradeProfit(lastTradeProfitInAccountCurrency); portfolio.AddTransactionRecord(security.LocalTime.ConvertToUtc( security.Exchange.TimeZone), lastTradeProfitInAccountCurrency - 2 * feeThisOrder); } //UPDATE HOLDINGS QUANTITY, AVG PRICE: //Currently NO holdings. The order is ALL our holdings. if (quantityHoldings == 0) { //First transaction just subtract order from cash and set our holdings: averageHoldingsPrice = fill.FillPrice; quantityHoldings = fill.FillQuantity; } else if (isLong) { //If we're currently LONG on the stock. switch (fill.Direction) { case OrderDirection.Buy: //Update the Holding Average Price: Total Value / Total Quantity: averageHoldingsPrice = ((averageHoldingsPrice * quantityHoldings) + (fill.FillQuantity * fill.FillPrice)) / (quantityHoldings + fill.FillQuantity); //Add the new quantity: quantityHoldings += fill.FillQuantity; break; case OrderDirection.Sell: quantityHoldings += fill.FillQuantity; //+ a short = a subtraction if (quantityHoldings < 0) { //If we've now passed through zero from selling stock: new avg price: averageHoldingsPrice = fill.FillPrice; } else if (quantityHoldings == 0) { averageHoldingsPrice = 0; } break; } } else if (isShort) { //We're currently SHORTING the stock: What is the new position now? switch (fill.Direction) { case OrderDirection.Buy: //Buying when we're shorting moves to close position: quantityHoldings += fill.FillQuantity; if (quantityHoldings > 0) { //If we were short but passed through zero, new average price is what we paid. The short position was closed. averageHoldingsPrice = fill.FillPrice; } else if (quantityHoldings == 0) { averageHoldingsPrice = 0; } break; case OrderDirection.Sell: //We are increasing a Short position: //E.g. -100 @ $5, adding -100 @ $10: Avg: $7.5 // dAvg = (-500 + -1000) / -200 = 7.5 averageHoldingsPrice = ((averageHoldingsPrice * quantityHoldings) + (fill.FillQuantity * fill.FillPrice)) / (quantityHoldings + fill.FillQuantity); quantityHoldings += fill.FillQuantity; break; } } } catch (Exception err) { Log.Error(err); } //Set the results back to the vehicle. security.Holdings.SetHoldings(averageHoldingsPrice, quantityHoldings); }
/// <summary> /// Performs application of an OrderEvent to the portfolio /// </summary> /// <param name="portfolio">The algorithm's portfolio</param> /// <param name="security">The fill's security</param> /// <param name="fill">The order event fill object to be applied</param> public virtual void ProcessFill(SecurityPortfolioManager portfolio, Security security, OrderEvent fill) { //Get the required information from the vehicle this order will affect var isLong = security.Holdings.IsLong; var isShort = security.Holdings.IsShort; var closedPosition = false; //Make local decimals to avoid any rounding errors from int multiplication var quantityHoldings = (decimal)security.Holdings.Quantity; var absoluteHoldingsQuantity = security.Holdings.AbsoluteQuantity; var averageHoldingsPrice = security.Holdings.AveragePrice; var lastTradeProfit = 0m; try { //Update the Vehicle approximate total sales volume. security.Holdings.AddNewSale(fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity)); //Get the Fee for this Order - Update the Portfolio Cash Balance: Remove Transacion Fees. var feeThisOrder = Math.Abs(fill.OrderFee); security.Holdings.AddNewFee(feeThisOrder); portfolio.CashBook[CashBook.AccountCurrency].AddAmount(-feeThisOrder); //Calculate & Update the Last Trade Profit if (isLong && fill.Direction == OrderDirection.Sell) { //Closing up a long position if (quantityHoldings >= fill.AbsoluteFillQuantity) { //Closing up towards Zero. lastTradeProfit = (fill.FillPrice - averageHoldingsPrice) * fill.AbsoluteFillQuantity; } else { //Closing up to Neg/Short Position (selling more than we have) - Only calc profit on the stock we have to sell. lastTradeProfit = (fill.FillPrice - averageHoldingsPrice) * quantityHoldings; } closedPosition = true; } else if (isShort && fill.Direction == OrderDirection.Buy) { //Closing up a short position. if (absoluteHoldingsQuantity >= fill.FillQuantity) { //Reducing the stock we have, and enough stock on hand to process order. lastTradeProfit = (averageHoldingsPrice - fill.FillPrice) * fill.AbsoluteFillQuantity; } else { //Increasing stock holdings, short to positive through zero, but only calc profit on stock we Buy. lastTradeProfit = (averageHoldingsPrice - fill.FillPrice) * absoluteHoldingsQuantity; } closedPosition = true; } if (closedPosition) { //Update Vehicle Profit Tracking: security.Holdings.AddNewProfit(lastTradeProfit); security.Holdings.SetLastTradeProfit(lastTradeProfit); portfolio.AddTransactionRecord(security.LocalTime.ConvertToUtc(security.Exchange.TimeZone), lastTradeProfit - 2 * feeThisOrder); } // Apply the funds using the current settlement model var amount = fill.FillPrice * Convert.ToDecimal(fill.FillQuantity); security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, CashBook.AccountCurrency, -amount); //UPDATE HOLDINGS QUANTITY, AVG PRICE: //Currently NO holdings. The order is ALL our holdings. if (quantityHoldings == 0) { //First transaction just subtract order from cash and set our holdings: averageHoldingsPrice = fill.FillPrice; quantityHoldings = fill.FillQuantity; } else if (isLong) { //If we're currently LONG on the stock. switch (fill.Direction) { case OrderDirection.Buy: //Update the Holding Average Price: Total Value / Total Quantity: averageHoldingsPrice = ((averageHoldingsPrice * quantityHoldings) + (fill.FillQuantity * fill.FillPrice)) / (quantityHoldings + (decimal)fill.FillQuantity); //Add the new quantity: quantityHoldings += fill.FillQuantity; //Subtract this order from cash: break; case OrderDirection.Sell: quantityHoldings += fill.FillQuantity; //+ a short = a subtraction if (quantityHoldings < 0) { //If we've now passed through zero from selling stock: new avg price: averageHoldingsPrice = fill.FillPrice; } else if (quantityHoldings == 0) { averageHoldingsPrice = 0; } break; } } else if (isShort) { //We're currently SHORTING the stock: What is the new position now? switch (fill.Direction) { case OrderDirection.Buy: //Buying when we're shorting moves to close position: quantityHoldings += fill.FillQuantity; if (quantityHoldings > 0) { //If we were short but passed through zero, new average price is what we paid. The short position was closed. averageHoldingsPrice = fill.FillPrice; } else if (quantityHoldings == 0) { averageHoldingsPrice = 0; } break; case OrderDirection.Sell: //We are increasing a Short position: //E.g. -100 @ $5, adding -100 @ $10: Avg: $7.5 // dAvg = (-500 + -1000) / -200 = 7.5 averageHoldingsPrice = ((averageHoldingsPrice * quantityHoldings) + (Convert.ToDecimal(fill.FillQuantity) * fill.FillPrice)) / (quantityHoldings + (decimal)fill.FillQuantity); quantityHoldings += fill.FillQuantity; break; } } } catch (Exception err) { Log.Error(err); } //Set the results back to the vehicle. security.Holdings.SetHoldings(averageHoldingsPrice, Convert.ToInt32(quantityHoldings)); }