/// <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 override void ProcessFill(SecurityPortfolioManager portfolio, Security security, OrderEvent fill) { var cfd = (Cfd) security; var quoteCurrency = cfd.QuoteCurrencySymbol; var quoteCash = portfolio.CashBook[quoteCurrency]; //Get the required information from the vehicle this order will affect var closedPosition = false; var isLong = security.Holdings.IsLong; var isShort = security.Holdings.IsShort; //Make local decimals to avoid any rounding errors from int multiplication var averageHoldingsPrice = security.Holdings.AveragePrice; var quantityHoldings = (decimal)security.Holdings.Quantity; var absoluteHoldingsQuantity = security.Holdings.AbsoluteQuantity; var lastTradeProfit = 0m; try { //Update the Vehicle approximate total sales volume. var saleValueInQuoteCurrency = fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity) * cfd.ContractMultiplier; var saleValue = saleValueInQuoteCurrency * quoteCash.ConversionRate; security.Holdings.AddNewSale(saleValue); //Get the Fee for this Order - Update the Portfolio Cash Balance: Remove Transaction Fees. var feeThisOrder = Math.Abs(fill.OrderFee); security.Holdings.AddNewFee(feeThisOrder); portfolio.CashBook[CashBook.AccountCurrency].AddAmount(-feeThisOrder); // Apply the funds using the current settlement model security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, quoteCurrency, -fill.FillQuantity * fill.FillPrice * cfd.ContractMultiplier); //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 -- this is in the quote currency lastTradeProfit = (fill.FillPrice - averageHoldingsPrice) * fill.AbsoluteFillQuantity * cfd.ContractMultiplier; } 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 * cfd.ContractMultiplier; } 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 * cfd.ContractMultiplier; } else { //Increasing stock holdings, short to positive through zero, but only calc profit on stock we Buy. lastTradeProfit = (averageHoldingsPrice - fill.FillPrice) * absoluteHoldingsQuantity * cfd.ContractMultiplier; } closedPosition = true; } if (closedPosition) { // convert the compute profit into the account currency lastTradeProfit *= quoteCash.ConversionRate; //Update Vehicle Profit Tracking: security.Holdings.AddNewProfit(lastTradeProfit); security.Holdings.SetLastTradeProfit(lastTradeProfit); portfolio.AddTransactionRecord(security.LocalTime.ConvertToUtc(security.Exchange.TimeZone), lastTradeProfit - 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, Convert.ToInt32(quantityHoldings)); }
public override void ProcessFill(SecurityPortfolioManager portfolio, Security security, OrderEvent fill) { // split the symbol into base and quote currencies string baseCurrency; string quoteCurrency; Forex.DecomposeCurrencyPair(security.Symbol, out baseCurrency, out quoteCurrency); // e.g. EUR GBP // EUR var baseCash = portfolio.CashBook[baseCurrency]; // GBP var quoteCash = portfolio.CashBook[quoteCurrency]; //Get the required information from the vehicle this order will affect var closedPosition = false; var isLong = security.Holdings.IsLong; var isShort = security.Holdings.IsShort; //Make local decimals to avoid any rounding errors from int multiplication var averageHoldingsPrice = security.Holdings.AveragePrice; var quantityHoldings = (decimal)security.Holdings.Quantity; var absoluteHoldingsQuantity = security.Holdings.AbsoluteQuantity; var lastTradeProfit = 0m; try { //Update the Vehicle approximate total sales volume. var saleValueInQuoteCurrency = fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity); var saleValue = saleValueInQuoteCurrency * quoteCash.ConversionRate; security.Holdings.AddNewSale(saleValue); //Get the Fee for this Order - Update the Portfolio Cash Balance: Remove Transaction Fees. var order = new MarketOrder(security.Symbol, fill.FillQuantity, security.Time, type: security.Type) { Price = fill.FillPrice }; var feeThisOrder = Math.Abs(security.TransactionModel.GetOrderFee(security, order)); security.Holdings.AddNewFee(feeThisOrder); portfolio.CashBook[CashBook.AccountCurrency].Quantity -= feeThisOrder; baseCash.Quantity += fill.FillQuantity; quoteCash.Quantity -= fill.FillQuantity * fill.FillPrice; //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 -- this is in the quote currency 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) { // convert the compute profit into the account currency lastTradeProfit *= quoteCash.ConversionRate; //Update Vehicle Profit Tracking: security.Holdings.AddNewProfit(lastTradeProfit); security.Holdings.SetLastTradeProfit(lastTradeProfit); portfolio.AddTransactionRecord(security.Time, lastTradeProfit - 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("ForexPortfolioManager.ProcessFill(orderEvent): " + err.Message); } //Set the results back to the vehicle. security.Holdings.SetHoldings(averageHoldingsPrice, Convert.ToInt32(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) { 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 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 security.SettlementModel.ApplyFunds(portfolio, security, fill.UtcTime, quoteCash.Symbol, -fill.FillQuantity * fill.FillPrice * security.SymbolProperties.ContractMultiplier); if (security.Type == SecurityType.Forex) { // model forex fills as currency swaps var forex = (Forex.Forex) 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 conversionFactor = security.QuoteCurrency.ConversionRate*security.SymbolProperties.ContractMultiplier; var lastTradeProfit = (closedSaleValueInQuoteCurrency - closedCost)*conversionFactor; //Update Vehicle Profit Tracking: security.Holdings.AddNewProfit(lastTradeProfit); security.Holdings.SetLastTradeProfit(lastTradeProfit); portfolio.AddTransactionRecord(security.LocalTime.ConvertToUtc(security.Exchange.TimeZone), lastTradeProfit - 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, Convert.ToInt32(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 order = new MarketOrder(security.Symbol, fill.FillQuantity, security.LocalTime.ConvertToUtc(security.Exchange.TimeZone), type: security.Type) {Price = fill.FillPrice, Status = OrderStatus.Filled}; var feeThisOrder = Math.Abs(security.TransactionModel.GetOrderFee(security, order)); security.Holdings.AddNewFee(feeThisOrder); portfolio.CashBook[CashBook.AccountCurrency].Quantity -= 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; //New cash += profitLoss + costOfAsset/leverage. portfolio.CashBook[CashBook.AccountCurrency].Quantity += lastTradeProfit + ((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; //New cash += profitLoss + costOfAsset/leverage. portfolio.CashBook[CashBook.AccountCurrency].Quantity += lastTradeProfit + ((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; //New cash += profitLoss + costOfAsset/leverage. portfolio.CashBook[CashBook.AccountCurrency].Quantity += lastTradeProfit + ((averageHoldingsPrice * fill.AbsoluteFillQuantity)); } else { //Increasing stock holdings, short to positive through zero, but only calc profit on stock we Buy. lastTradeProfit = (averageHoldingsPrice - fill.FillPrice) * absoluteHoldingsQuantity; //New cash += profitLoss + costOfAsset/leverage. portfolio.CashBook[CashBook.AccountCurrency].Quantity += lastTradeProfit + ((averageHoldingsPrice * 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); } //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; portfolio.CashBook[CashBook.AccountCurrency].Quantity -= (fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity)); } 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: portfolio.CashBook[CashBook.AccountCurrency].Quantity -= (fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity)); 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; portfolio.CashBook[CashBook.AccountCurrency].Quantity -= (fill.FillPrice * Math.Abs(quantityHoldings)); } 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; portfolio.CashBook[CashBook.AccountCurrency].Quantity -= (fill.FillPrice * Math.Abs(quantityHoldings)); } 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; portfolio.CashBook[CashBook.AccountCurrency].Quantity -= (fill.FillPrice * Convert.ToDecimal(fill.AbsoluteFillQuantity)); break; } } } catch (Exception err) { Log.Error("SecurityPortfolioModel.ProcessFill(): " + err.Message); } //Set the results back to the vehicle. security.Holdings.SetHoldings(averageHoldingsPrice, Convert.ToInt32(quantityHoldings)); }