Lean  $LEAN_TAG$
SecurityPortfolioModel.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using QuantConnect.Orders;
18 using QuantConnect.Logging;
20 
22 {
23  /// <summary>
24  /// Provides a default implementation of <see cref="ISecurityPortfolioModel"/> that simply
25  /// applies the fills to the algorithm's portfolio. This implementation is intended to
26  /// handle all security types.
27  /// </summary>
29  {
30  /// <summary>
31  /// Performs application of an OrderEvent to the portfolio
32  /// </summary>
33  /// <param name="portfolio">The algorithm's portfolio</param>
34  /// <param name="security">The fill's security</param>
35  /// <param name="fill">The order event fill object to be applied</param>
36  public virtual void ProcessFill(SecurityPortfolioManager portfolio, Security security, OrderEvent fill)
37  {
38  var quoteCash = security.QuoteCurrency;
39 
40  //Get the required information from the vehicle this order will affect
41  var isLong = security.Holdings.IsLong;
42  var isShort = security.Holdings.IsShort;
43  var closedPosition = false;
44  //Make local decimals to avoid any rounding errors from int multiplication
45  var quantityHoldings = (decimal)security.Holdings.Quantity;
46  var averageHoldingsPrice = security.Holdings.AveragePrice;
47 
48  try
49  {
50  // apply sales value to holdings in the account currency
51  var saleValue = security.Holdings.GetQuantityValue(fill.AbsoluteFillQuantity, fill.FillPrice).InAccountCurrency;
52  security.Holdings.AddNewSale(saleValue);
53 
54  // subtract transaction fees from the portfolio
55  var feeInAccountCurrency = 0m;
56  if (fill.OrderFee != OrderFee.Zero
57  // this is for user friendliness because some
58  // Security types default to use 0 USD ConstantFeeModel
59  && fill.OrderFee.Value.Amount != 0)
60  {
61  var feeThisOrder = fill.OrderFee.Value;
62  feeInAccountCurrency = portfolio.CashBook.ConvertToAccountCurrency(feeThisOrder).Amount;
63  security.Holdings.AddNewFee(feeInAccountCurrency);
64 
65  fill.OrderFee.ApplyToPortfolio(portfolio, fill);
66  }
67 
68  // apply the funds using the current settlement model
69  // we dont adjust funds for futures and CFDs: it is zero upfront payment derivative (margin applies though)
70  // We do however apply funds for futures options, pay/gained premium, since they affect our cash balance the moment they are purchased/sold.
71  if (security.Type != SecurityType.Future && security.Type != SecurityType.Cfd && security.Type != SecurityType.CryptoFuture)
72  {
73  security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(portfolio, security, fill.UtcTime, new CashAmount(-fill.FillQuantity * fill.FillPrice * security.SymbolProperties.ContractMultiplier, quoteCash.Symbol), fill));
74  }
75  if (security.Type == SecurityType.Forex || security.Type == SecurityType.Crypto)
76  {
77  // model forex fills as currency swaps
78  var forex = (IBaseCurrencySymbol) security;
79  security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(portfolio, security, fill.UtcTime, new CashAmount(fill.FillQuantity, forex.BaseCurrency.Symbol), fill));
80  }
81 
82  // did we close or open a position further?
83  closedPosition = isLong && fill.Direction == OrderDirection.Sell
84  || isShort && fill.Direction == OrderDirection.Buy;
85 
86  // calculate the last trade profit
87  if (closedPosition)
88  {
89  var lastTradeProfit = ProcessCloseTradeProfit(portfolio, security, fill);
90 
91  //Update Vehicle Profit Tracking:
92  var lastTradeProfitInAccountCurrency = lastTradeProfit.InAccountCurrency;
93  security.Holdings.AddNewProfit(lastTradeProfitInAccountCurrency);
94  security.Holdings.SetLastTradeProfit(lastTradeProfitInAccountCurrency);
95  var transactionProfitLoss = lastTradeProfitInAccountCurrency - 2 * feeInAccountCurrency;
96  portfolio.AddTransactionRecord(
97  security.LocalTime.ConvertToUtc(security.Exchange.TimeZone),
98  transactionProfitLoss,
99  fill.IsWin(security, transactionProfitLoss));
100  }
101 
102  //UPDATE HOLDINGS QUANTITY, AVG PRICE:
103  //Currently NO holdings. The order is ALL our holdings.
104  if (quantityHoldings == 0)
105  {
106  //First transaction just subtract order from cash and set our holdings:
107  averageHoldingsPrice = fill.FillPrice;
108  quantityHoldings = fill.FillQuantity;
109  }
110  else if (isLong)
111  {
112  //If we're currently LONG on the stock.
113  switch (fill.Direction)
114  {
115  case OrderDirection.Buy:
116  //Update the Holding Average Price: Total Value / Total Quantity:
117  averageHoldingsPrice = ((averageHoldingsPrice*quantityHoldings) + (fill.FillQuantity*fill.FillPrice))/(quantityHoldings + fill.FillQuantity);
118  //Add the new quantity:
119  quantityHoldings += fill.FillQuantity;
120  break;
121 
122  case OrderDirection.Sell:
123  quantityHoldings += fill.FillQuantity; //+ a short = a subtraction
124  if (quantityHoldings < 0)
125  {
126  //If we've now passed through zero from selling stock: new avg price:
127  averageHoldingsPrice = fill.FillPrice;
128  }
129  else if (quantityHoldings == 0)
130  {
131  averageHoldingsPrice = 0;
132  }
133  break;
134  }
135  }
136  else if (isShort)
137  {
138  //We're currently SHORTING the stock: What is the new position now?
139  switch (fill.Direction)
140  {
141  case OrderDirection.Buy:
142  //Buying when we're shorting moves to close position:
143  quantityHoldings += fill.FillQuantity;
144  if (quantityHoldings > 0)
145  {
146  //If we were short but passed through zero, new average price is what we paid. The short position was closed.
147  averageHoldingsPrice = fill.FillPrice;
148  }
149  else if (quantityHoldings == 0)
150  {
151  averageHoldingsPrice = 0;
152  }
153  break;
154 
155  case OrderDirection.Sell:
156  //We are increasing a Short position:
157  //E.g. -100 @ $5, adding -100 @ $10: Avg: $7.5
158  // dAvg = (-500 + -1000) / -200 = 7.5
159  averageHoldingsPrice = ((averageHoldingsPrice*quantityHoldings) + (fill.FillQuantity*fill.FillPrice))/(quantityHoldings + fill.FillQuantity);
160  quantityHoldings += fill.FillQuantity;
161  break;
162  }
163  }
164  }
165  catch (Exception err)
166  {
167  Log.Error(err);
168  }
169 
170  //Set the results back to the vehicle.
171  security.Holdings.SetHoldings(averageHoldingsPrice, quantityHoldings);
172  }
173 
174  /// <summary>
175  /// Helper method to determine the close trade profit
176  /// </summary>
178  {
179  var absoluteHoldingsQuantity = security.Holdings.AbsoluteQuantity;
180 
181  // profit = (closed sale value - cost)*conversion to account currency
182  // closed sale value = quantity closed * fill price BUYs are deemed negative cash flow
183  // cost = quantity closed * average holdings price SELLS are deemed positive cash flow
184  var absoluteQuantityClosed = Math.Min(fill.AbsoluteFillQuantity, absoluteHoldingsQuantity);
185  var quantityClosed = Math.Sign(-fill.FillQuantity) * absoluteQuantityClosed;
186  var closedCost = security.Holdings.GetQuantityValue(quantityClosed, security.Holdings.AveragePrice);
187  var closedSaleValueInQuoteCurrency = security.Holdings.GetQuantityValue(quantityClosed, fill.FillPrice);
188 
189  var lastTradeProfit = new ConvertibleCashAmount(closedSaleValueInQuoteCurrency.Amount - closedCost.Amount, closedSaleValueInQuoteCurrency.Cash);
190 
191  // Reflect account cash adjustment for futures/CFD position
192  if (security.Type == SecurityType.Future || security.Type == SecurityType.Cfd || security.Type == SecurityType.CryptoFuture)
193  {
194  security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(portfolio, security, fill.UtcTime, lastTradeProfit, fill));
195  }
196 
197  return lastTradeProfit;
198  }
199  }
200 }