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 absoluteHoldingsQuantity = security.Holdings.AbsoluteQuantity;
47  var averageHoldingsPrice = security.Holdings.AveragePrice;
48 
49  try
50  {
51  // apply sales value to holdings in the account currency
52  var saleValue = security.Holdings.GetQuantityValue(fill.AbsoluteFillQuantity, fill.FillPrice).InAccountCurrency;
53  security.Holdings.AddNewSale(saleValue);
54 
55  // subtract transaction fees from the portfolio
56  var feeInAccountCurrency = 0m;
57  if (fill.OrderFee != OrderFee.Zero
58  // this is for user friendliness because some
59  // Security types default to use 0 USD ConstantFeeModel
60  && fill.OrderFee.Value.Amount != 0)
61  {
62  var feeThisOrder = fill.OrderFee.Value;
63  feeInAccountCurrency = portfolio.CashBook.ConvertToAccountCurrency(feeThisOrder).Amount;
64  security.Holdings.AddNewFee(feeInAccountCurrency);
65 
66  fill.OrderFee.ApplyToPortfolio(portfolio, fill);
67  }
68 
69  // apply the funds using the current settlement model
70  // we dont adjust funds for futures and CFDs: it is zero upfront payment derivative (margin applies though)
71  // We do however apply funds for futures options, pay/gained premium, since they affect our cash balance the moment they are purchased/sold.
72  if (security.Type != SecurityType.Future && security.Type != SecurityType.Cfd && security.Type != SecurityType.CryptoFuture)
73  {
74  security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(portfolio, security, fill.UtcTime, new CashAmount(-fill.FillQuantity * fill.FillPrice * security.SymbolProperties.ContractMultiplier, quoteCash.Symbol), fill));
75  }
76  if (security.Type == SecurityType.Forex || security.Type == SecurityType.Crypto)
77  {
78  // model forex fills as currency swaps
79  var forex = (IBaseCurrencySymbol) security;
80  security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(portfolio, security, fill.UtcTime, new CashAmount(fill.FillQuantity, forex.BaseCurrency.Symbol), fill));
81  }
82 
83  // did we close or open a position further?
84  closedPosition = isLong && fill.Direction == OrderDirection.Sell
85  || isShort && fill.Direction == OrderDirection.Buy;
86 
87  // calculate the last trade profit
88  if (closedPosition)
89  {
90  // profit = (closed sale value - cost)*conversion to account currency
91  // closed sale value = quantity closed * fill price BUYs are deemed negative cash flow
92  // cost = quantity closed * average holdings price SELLS are deemed positive cash flow
93  var absoluteQuantityClosed = Math.Min(fill.AbsoluteFillQuantity, absoluteHoldingsQuantity);
94  var quantityClosed = Math.Sign(-fill.FillQuantity) * absoluteQuantityClosed;
95  var closedCost = security.Holdings.GetQuantityValue(quantityClosed, averageHoldingsPrice);
96  var closedSaleValueInQuoteCurrency = security.Holdings.GetQuantityValue(quantityClosed, fill.FillPrice);
97 
98  var lastTradeProfit = closedSaleValueInQuoteCurrency.Amount - closedCost.Amount;
99  var lastTradeProfitInAccountCurrency = closedSaleValueInQuoteCurrency.InAccountCurrency - closedCost.InAccountCurrency;
100 
101  // Reflect account cash adjustment for futures/CFD position
102  if (security.Type == SecurityType.Future || security.Type == SecurityType.Cfd || security.Type == SecurityType.CryptoFuture)
103  {
104  security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(portfolio, security, fill.UtcTime, new CashAmount(lastTradeProfit, closedCost.Cash.Symbol), fill));
105  }
106 
107  //Update Vehicle Profit Tracking:
108  security.Holdings.AddNewProfit(lastTradeProfitInAccountCurrency);
109  security.Holdings.SetLastTradeProfit(lastTradeProfitInAccountCurrency);
110  var transactionProfitLoss = lastTradeProfitInAccountCurrency - 2 * feeInAccountCurrency;
111  portfolio.AddTransactionRecord(
112  security.LocalTime.ConvertToUtc(security.Exchange.TimeZone),
113  transactionProfitLoss,
114  fill.IsWin(security, transactionProfitLoss));
115  }
116 
117  //UPDATE HOLDINGS QUANTITY, AVG PRICE:
118  //Currently NO holdings. The order is ALL our holdings.
119  if (quantityHoldings == 0)
120  {
121  //First transaction just subtract order from cash and set our holdings:
122  averageHoldingsPrice = fill.FillPrice;
123  quantityHoldings = fill.FillQuantity;
124  }
125  else if (isLong)
126  {
127  //If we're currently LONG on the stock.
128  switch (fill.Direction)
129  {
130  case OrderDirection.Buy:
131  //Update the Holding Average Price: Total Value / Total Quantity:
132  averageHoldingsPrice = ((averageHoldingsPrice*quantityHoldings) + (fill.FillQuantity*fill.FillPrice))/(quantityHoldings + fill.FillQuantity);
133  //Add the new quantity:
134  quantityHoldings += fill.FillQuantity;
135  break;
136 
137  case OrderDirection.Sell:
138  quantityHoldings += fill.FillQuantity; //+ a short = a subtraction
139  if (quantityHoldings < 0)
140  {
141  //If we've now passed through zero from selling stock: new avg price:
142  averageHoldingsPrice = fill.FillPrice;
143  }
144  else if (quantityHoldings == 0)
145  {
146  averageHoldingsPrice = 0;
147  }
148  break;
149  }
150  }
151  else if (isShort)
152  {
153  //We're currently SHORTING the stock: What is the new position now?
154  switch (fill.Direction)
155  {
156  case OrderDirection.Buy:
157  //Buying when we're shorting moves to close position:
158  quantityHoldings += fill.FillQuantity;
159  if (quantityHoldings > 0)
160  {
161  //If we were short but passed through zero, new average price is what we paid. The short position was closed.
162  averageHoldingsPrice = fill.FillPrice;
163  }
164  else if (quantityHoldings == 0)
165  {
166  averageHoldingsPrice = 0;
167  }
168  break;
169 
170  case OrderDirection.Sell:
171  //We are increasing a Short position:
172  //E.g. -100 @ $5, adding -100 @ $10: Avg: $7.5
173  // dAvg = (-500 + -1000) / -200 = 7.5
174  averageHoldingsPrice = ((averageHoldingsPrice*quantityHoldings) + (fill.FillQuantity*fill.FillPrice))/(quantityHoldings + fill.FillQuantity);
175  quantityHoldings += fill.FillQuantity;
176  break;
177  }
178  }
179  }
180  catch (Exception err)
181  {
182  Log.Error(err);
183  }
184 
185  //Set the results back to the vehicle.
186  security.Holdings.SetHoldings(averageHoldingsPrice, quantityHoldings);
187  }
188  }
189 }