/******************************************************** * CLASS CONSTRUCTOR *********************************************************/ /// <summary> /// Initialise the Algorithm /// </summary> public QCAlgorithm() { //Initialise the Algorithm Helper Classes: //- Note - ideally these wouldn't be here, but because of the DLL we need to make the classes shared across // the Worker & Algorithm, limiting ability to do anything else. Securities = new SecurityManager(); Transacions = new SecurityTransactionManager(Securities); Portfolio = new SecurityPortfolioManager(Securities, Transacions); //Initialise Data Manager DataManager = new DataManager(); //Initialise Error and Order Holders: Errors = new List<string>(); //Initialise Algorithm RunMode to Automatic: _runMode = RunMode.Automatic; //Initialise to unlocked: _locked = false; //Initialise Start and End Dates: _startDate = new DateTime(); _endDate = new DateTime(); }
/// <summary> /// Applies cash settlement rules /// </summary> /// <param name="portfolio">The algorithm's portfolio</param> /// <param name="security">The fill's security</param> /// <param name="applicationTimeUtc">The fill time (in UTC)</param> /// <param name="currency">The currency symbol</param> /// <param name="amount">The amount of cash to apply</param> public void ApplyFunds(SecurityPortfolioManager portfolio, Security security, DateTime applicationTimeUtc, string currency, decimal amount) { if (amount > 0) { // positive amount: sell order filled portfolio.UnsettledCashBook[currency].AddAmount(amount); // find the correct settlement date (usually T+3 or T+1) var settlementDate = applicationTimeUtc.ConvertFromUtc(security.Exchange.TimeZone).Date; for (var i = 0; i < _numberOfDays; i++) { settlementDate = settlementDate.AddDays(1); // only count days when market is open if (!security.Exchange.Hours.IsDateOpen(settlementDate)) i--; } // use correct settlement time var settlementTimeUtc = settlementDate.Add(_timeOfDay).ConvertToUtc(security.Exchange.Hours.TimeZone); portfolio.AddUnsettledCashAmount(new UnsettledCashAmount(settlementTimeUtc, currency, amount)); } else { // negative amount: buy order filled portfolio.CashBook[currency].AddAmount(amount); } }
public void FundsAreSettledImmediately() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); var model = new ImmediateSettlementModel(); var config = CreateTradeBarConfig(); var security = new Security(SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours(), config); portfolio.SetCash(1000); Assert.AreEqual(1000, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); var timeUtc = Noon.ConvertToUtc(TimeZones.NewYork); model.ApplyFunds(portfolio, security, timeUtc, "USD", 1000); Assert.AreEqual(2000, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); model.ApplyFunds(portfolio, security, timeUtc, "USD", -500); Assert.AreEqual(1500, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); model.ApplyFunds(portfolio, security, timeUtc, "USD", 1000); Assert.AreEqual(2500, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); }
public void SellOnThursdaySettleOnTuesday() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); // settlement at T+3, 8:00 AM var model = new DelayedSettlementModel(3, TimeSpan.FromHours(8)); var config = CreateTradeBarConfig(Symbols.SPY); var security = new Security(SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours(), config, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency)); portfolio.SetCash(3000); Assert.AreEqual(3000, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); // Sell on Thursday var timeUtc = Noon.AddDays(3).ConvertToUtc(TimeZones.NewYork); model.ApplyFunds(portfolio, security, timeUtc, "USD", 1000); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(3000, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Friday, still unsettled timeUtc = timeUtc.AddDays(1); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(3000, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Saturday, still unsettled timeUtc = timeUtc.AddDays(1); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(3000, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Sunday, still unsettled timeUtc = timeUtc.AddDays(1); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(3000, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Monday, still unsettled timeUtc = timeUtc.AddDays(1); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(3000, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Tuesday at 7:55 AM, still unsettled timeUtc = timeUtc.AddDays(1).AddHours(-4).AddMinutes(-5); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(3000, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Tuesday at 8 AM, now settled timeUtc = timeUtc.AddMinutes(5); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(4000, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); }
public void TestCashFills() { // this test asserts the portfolio behaves according to the Test_Cash algo, see TestData\CashTestingStrategy.csv // also "https://www.dropbox.com/s/oiliumoyqqj1ovl/2013-cash.csv?dl=1" const string fillsFile = "TestData\\test_cash_fills.xml"; const string equityFile = "TestData\\test_cash_equity.xml"; var fills = XDocument.Load(fillsFile).Descendants("OrderEvent").Select(x => new OrderEvent( x.Get<int>("OrderId"), SymbolMap[x.Get<string>("Symbol")], DateTime.MinValue, x.Get<OrderStatus>("Status"), x.Get<int>("FillQuantity") < 0 ? OrderDirection.Sell : x.Get<int>("FillQuantity") > 0 ? OrderDirection.Buy : OrderDirection.Hold, x.Get<decimal>("FillPrice"), x.Get<int>("FillQuantity"), 0m) ).ToList(); var equity = XDocument.Load(equityFile).Descendants("decimal") .Select(x => decimal.Parse(x.Value, CultureInfo.InvariantCulture)) .ToList(); Assert.AreEqual(fills.Count + 1, equity.Count); // we're going to process fills and very our equity after each fill var subscriptions = new SubscriptionManager(TimeKeeper); var securities = new SecurityManager(TimeKeeper); var security = new Security(SecurityExchangeHours, subscriptions.Add(CASH, Resolution.Daily, TimeZones.NewYork, TimeZones.NewYork)); security.SetLeverage(10m); securities.Add(CASH, security); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(equity[0]); for (int i = 0; i < fills.Count; i++) { // before processing the fill we must deduct the cost var fill = fills[i]; var time = DateTime.Today.AddDays(i); TimeKeeper.SetUtcDateTime(time.ConvertToUtc(TimeZones.NewYork)); // the value of 'CASH' increments for each fill, the original test algo did this monthly // the time doesn't really matter though security.SetMarketPrice(new IndicatorDataPoint(CASH, time, i + 1)); portfolio.ProcessFill(fill); Assert.AreEqual(equity[i + 1], portfolio.TotalPortfolioValue, "Failed on " + i); } }
/// <summary> /// Check if there is sufficient capital to execute this order. /// </summary> /// <param name="portfolio">Our portfolio</param> /// <param name="order">Order we're checking</param> /// <returns>True if suficient capital.</returns> public bool GetSufficientCapitalForOrder(SecurityPortfolioManager portfolio, Order order) { var security = _securities[order.Symbol]; var freeMargin = security.MarginModel.GetMarginRemaining(portfolio, security, order.Direction); var initialMarginRequiredForOrder = security.MarginModel.GetInitialMarginRequiredForOrder(security, order); if (Math.Abs(initialMarginRequiredForOrder) > freeMargin) { Log.Error(string.Format("Transactions.GetSufficientCapitalForOrder(): Id: {0}, Initial Margin: {1}, Free Margin: {2}", order.Id, initialMarginRequiredForOrder, freeMargin)); return false; } return true; }
public new decimal GetMarginRemaining(SecurityPortfolioManager portfolio, Security security, OrderDirection direction) { return(base.GetMarginRemaining(portfolio, security, direction)); }
public void ComputeMarginProperlyShortCoverZeroLong() { const decimal leverage = 2m; const int amount = 1000; const int quantity = (int)(amount * leverage); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var orderProcessor = new OrderProcessor(); transactions.SetOrderProcessor(orderProcessor); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].SetAmount(amount); var config = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.AAPL]; security.SetLeverage(leverage); var time = DateTime.Now; const decimal sellPrice = 1m; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, sellPrice, sellPrice, sellPrice, sellPrice, 1)); var order = new MarketOrder(Symbols.AAPL, -quantity, time) { Price = sellPrice }; var fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = sellPrice, FillQuantity = -quantity }; orderProcessor.AddOrder(order); var request = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, order.Quantity, 0, 0, order.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); portfolio.ProcessFill(fill); // we shouldn't be able to place a new short order var newOrder = new MarketOrder(Symbols.AAPL, -1, time.AddSeconds(1)) { Price = sellPrice }; var sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); // we should be able to place cover to zero newOrder = new MarketOrder(Symbols.AAPL, quantity, time.AddSeconds(1)) { Price = sellPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsTrue(sufficientCapital); // now the stock doubles, so we should have negative margin remaining time = time.AddDays(1); const decimal highPrice = sellPrice * 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, highPrice, highPrice, highPrice, highPrice, 1)); // we still shouldn be able to place cover to zero newOrder = new MarketOrder(Symbols.AAPL, quantity, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsTrue(sufficientCapital); // we shouldn't be able to place cover to long newOrder = new MarketOrder(Symbols.AAPL, quantity + 1, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); }
public void ForexFillUpdatesCashCorrectly() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(1000); portfolio.CashBook.Add("EUR", 0, 1.1000m); securities.Add(Symbols.EURUSD, new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, portfolio.CashBook["USD"], CreateTradeBarDataConfig(SecurityType.Forex, Symbols.EURUSD), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.EURUSD]; Assert.AreEqual(0, security.Holdings.Quantity); Assert.AreEqual(1000, portfolio.Cash); var orderFee = security.FeeModel.GetOrderFee(security, new MarketOrder(Symbols.EURUSD, 100, DateTime.MinValue)); var fill = new OrderEvent(1, Symbols.EURUSD, DateTime.MinValue, OrderStatus.Filled, OrderDirection.Buy, 1.1000m, 100, orderFee); portfolio.ProcessFill(fill); Assert.AreEqual(100, security.Holdings.Quantity); Assert.AreEqual(998, portfolio.Cash); Assert.AreEqual(100, portfolio.CashBook["EUR"].Amount); Assert.AreEqual(888, portfolio.CashBook["USD"].Amount); }
public void MarginComputesProperlyWithMultipleSecurities() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var orderProcessor = new OrderProcessor(); transactions.SetOrderProcessor(orderProcessor); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].SetAmount(1000); portfolio.CashBook.Add("EUR", 1000, 1.1m); portfolio.CashBook.Add("GBP", -1000, 2.0m); var eurCash = portfolio.CashBook["EUR"]; var gbpCash = portfolio.CashBook["GBP"]; var usdCash = portfolio.CashBook["USD"]; var time = DateTime.Now; var config1 = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config1, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); securities[Symbols.AAPL].SetLeverage(2m); securities[Symbols.AAPL].Holdings.SetHoldings(100, 100); securities[Symbols.AAPL].SetMarketPrice(new TradeBar{Time = time, Value = 100}); //Console.WriteLine("AAPL TMU: " + securities[Symbols.AAPL].MarginModel.GetMaintenanceMargin(securities[Symbols.AAPL])); //Console.WriteLine("AAPL Value: " + securities[Symbols.AAPL].Holdings.HoldingsValue); //Console.WriteLine(); var config2 = CreateTradeBarDataConfig(SecurityType.Forex, Symbols.EURUSD); securities.Add(new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, usdCash, config2, SymbolProperties.GetDefault(CashBook.AccountCurrency))); securities[Symbols.EURUSD].SetLeverage(100m); securities[Symbols.EURUSD].Holdings.SetHoldings(1.1m, 1000); securities[Symbols.EURUSD].SetMarketPrice(new TradeBar { Time = time, Value = 1.1m }); //Console.WriteLine("EURUSD TMU: " + securities[Symbols.EURUSD].MarginModel.GetMaintenanceMargin(securities[Symbols.EURUSD])); //Console.WriteLine("EURUSD Value: " + securities[Symbols.EURUSD].Holdings.HoldingsValue); //Console.WriteLine(); var config3 = CreateTradeBarDataConfig(SecurityType.Forex, Symbols.EURGBP); securities.Add(new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, gbpCash, config3, SymbolProperties.GetDefault(gbpCash.Symbol))); securities[Symbols.EURGBP].SetLeverage(100m); securities[Symbols.EURGBP].Holdings.SetHoldings(1m, 1000); securities[Symbols.EURGBP].SetMarketPrice(new TradeBar { Time = time, Value = 1m }); //Console.WriteLine("EURGBP TMU: " + securities[Symbols.EURGBP].MarginModel.GetMaintenanceMargin(securities[Symbols.EURGBP])); //Console.WriteLine("EURGBP Value: " + securities[Symbols.EURGBP].Holdings.HoldingsValue); //Console.WriteLine(); //Console.WriteLine(portfolio.CashBook["USD"]); //Console.WriteLine(portfolio.CashBook["EUR"]); //Console.WriteLine(portfolio.CashBook["GBP"]); //Console.WriteLine("CashBook: " + portfolio.CashBook.TotalValueInAccountCurrency); //Console.WriteLine(); //Console.WriteLine("Total Margin Used: " + portfolio.TotalMarginUsed); //Console.WriteLine("Total Free Margin: " + portfolio.MarginRemaining); //Console.WriteLine("Total Portfolio Value: " + portfolio.TotalPortfolioValue); var acceptedOrder = new MarketOrder(Symbols.AAPL, 101, DateTime.Now) { Price = 100 }; orderProcessor.AddOrder(acceptedOrder); var request = new SubmitOrderRequest(OrderType.Market, acceptedOrder.SecurityType, acceptedOrder.Symbol, acceptedOrder.Quantity, 0, 0, acceptedOrder.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); var sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, acceptedOrder); Assert.IsTrue(sufficientCapital); var rejectedOrder = new MarketOrder(Symbols.AAPL, 102, DateTime.Now) { Price = 100 }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, rejectedOrder); Assert.IsFalse(sufficientCapital); }
public void ForexCashFills() { // this test asserts the portfolio behaves according to the Test_Cash algo, but for a Forex security, // see TestData\CashTestingStrategy.csv; also "https://www.dropbox.com/s/oiliumoyqqj1ovl/2013-cash.csv?dl=1" const string fillsFile = "TestData\\test_forex_fills.xml"; const string equityFile = "TestData\\test_forex_equity.xml"; const string mchQuantityFile = "TestData\\test_forex_fills_mch_quantity.xml"; const string jwbQuantityFile = "TestData\\test_forex_fills_jwb_quantity.xml"; var fills = XDocument.Load(fillsFile).Descendants("OrderEvent").Select(x => new OrderEvent( x.Get<int>("OrderId"), SymbolMap[x.Get<string>("Symbol")], DateTime.MinValue, x.Get<OrderStatus>("Status"), x.Get<int>("FillQuantity") < 0 ? OrderDirection.Sell : x.Get<int>("FillQuantity") > 0 ? OrderDirection.Buy : OrderDirection.Hold, x.Get<decimal>("FillPrice"), x.Get<int>("FillQuantity"), 0) ).ToList(); var equity = XDocument.Load(equityFile).Descendants("decimal") .Select(x => decimal.Parse(x.Value, CultureInfo.InvariantCulture)) .ToList(); var mchQuantity = XDocument.Load(mchQuantityFile).Descendants("decimal") .Select(x => decimal.Parse(x.Value, CultureInfo.InvariantCulture)) .ToList(); var jwbQuantity = XDocument.Load(jwbQuantityFile).Descendants("decimal") .Select(x => decimal.Parse(x.Value, CultureInfo.InvariantCulture)) .ToList(); Assert.AreEqual(fills.Count + 1, equity.Count); // we're going to process fills and very our equity after each fill var subscriptions = new SubscriptionManager(TimeKeeper); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(equity[0]); portfolio.CashBook.Add("MCH", mchQuantity[0], 0); portfolio.CashBook.Add("JWB", jwbQuantity[0], 0); var jwbCash = portfolio.CashBook["JWB"]; var mchCash = portfolio.CashBook["MCH"]; var usdCash = portfolio.CashBook["USD"]; var mchJwbSecurity = new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, jwbCash, subscriptions.Add(MCHJWB, Resolution.Minute, TimeZones.NewYork, TimeZones.NewYork), SymbolProperties.GetDefault(jwbCash.Symbol)); mchJwbSecurity.SetLeverage(10m); var mchUsdSecurity = new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, usdCash, subscriptions.Add(MCHUSD, Resolution.Minute, TimeZones.NewYork, TimeZones.NewYork), SymbolProperties.GetDefault(usdCash.Symbol)); mchUsdSecurity.SetLeverage(10m); var usdJwbSecurity = new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, mchCash, subscriptions.Add(USDJWB, Resolution.Minute, TimeZones.NewYork, TimeZones.NewYork), SymbolProperties.GetDefault(mchCash.Symbol)); usdJwbSecurity.SetLeverage(10m); // no fee model mchJwbSecurity.TransactionModel = new SecurityTransactionModel(); mchUsdSecurity.TransactionModel = new SecurityTransactionModel(); usdJwbSecurity.TransactionModel = new SecurityTransactionModel(); securities.Add(mchJwbSecurity); securities.Add(usdJwbSecurity); securities.Add(mchUsdSecurity); portfolio.CashBook.EnsureCurrencyDataFeeds(securities, subscriptions, MarketHoursDatabase.FromDataFolder(), SymbolPropertiesDatabase.FromDataFolder(), DefaultBrokerageModel.DefaultMarketMap); for (int i = 0; i < fills.Count; i++) { // before processing the fill we must deduct the cost var fill = fills[i]; var time = DateTime.Today.AddDays(i); // the value of 'MCJWB' increments for each fill, the original test algo did this monthly // the time doesn't really matter though decimal mchJwb = i + 1; decimal mchUsd = (i + 1)/(i + 2m); decimal usdJwb = i + 2; Assert.AreEqual((double)mchJwb, (double)(mchUsd*usdJwb), 1e-10); //Console.WriteLine("Step: " + i + " -- MCHJWB: " + mchJwb); jwbCash.Update(new IndicatorDataPoint(MCHJWB, time, mchJwb)); usdCash.Update(new IndicatorDataPoint(MCHUSD, time, mchUsd)); mchCash.Update(new IndicatorDataPoint(JWBUSD, time, usdJwb)); var updateData = new Dictionary<Security, BaseData> { {mchJwbSecurity, new IndicatorDataPoint(MCHJWB, time, mchJwb)}, {mchUsdSecurity, new IndicatorDataPoint(MCHUSD, time, mchUsd)}, {usdJwbSecurity, new IndicatorDataPoint(JWBUSD, time, usdJwb)} }; foreach (var kvp in updateData) { kvp.Key.SetMarketPrice(kvp.Value); } portfolio.ProcessFill(fill); //Console.WriteLine("-----------------------"); //Console.WriteLine(fill); //Console.WriteLine("Post step: " + i); //foreach (var cash in portfolio.CashBook) //{ // Console.WriteLine(cash.Value); //} //Console.WriteLine("CashValue: " + portfolio.CashBook.TotalValueInAccountCurrency); Console.WriteLine(i + 1 + " " + portfolio.TotalPortfolioValue.ToString("C")); //Assert.AreEqual((double) equity[i + 1], (double)portfolio.TotalPortfolioValue, 2e-2); Assert.AreEqual((double) mchQuantity[i + 1], (double)portfolio.CashBook["MCH"].Amount); Assert.AreEqual((double) jwbQuantity[i + 1], (double)portfolio.CashBook["JWB"].Amount); //Console.WriteLine(); //Console.WriteLine(); } }
/// <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)); }
public void MarginComputesProperlyWithMultipleSecurities() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].Quantity = 1000; portfolio.CashBook.Add("EUR", 1000, 1.1m); portfolio.CashBook.Add("GBP", -1000, 2.0m); var eurCash = portfolio.CashBook["EUR"]; var gbpCash = portfolio.CashBook["GBP"]; var usdCash = portfolio.CashBook["USD"]; var time = DateTime.Now; var config1 = CreateTradeBarDataConfig(SecurityType.Equity, "AAPL"); securities.Add(new Security(SecurityExchangeHours, config1, 2)); securities["AAPL"].Holdings.SetHoldings(100, 100); securities["AAPL"].SetMarketPrice(new TradeBar{Time = time, Value = 100}); //Console.WriteLine("AAPL TMU: " + securities["AAPL"].MarginModel.GetMaintenanceMargin(securities["AAPL"])); //Console.WriteLine("AAPL Value: " + securities["AAPL"].Holdings.HoldingsValue); //Console.WriteLine(); var config2 = CreateTradeBarDataConfig(SecurityType.Forex, "EURUSD"); securities.Add(new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, usdCash, config2, 100)); securities["EURUSD"].Holdings.SetHoldings(1.1m, 1000); securities["EURUSD"].SetMarketPrice(new TradeBar { Time = time, Value = 1.1m }); //Console.WriteLine("EURUSD TMU: " + securities["EURUSD"].MarginModel.GetMaintenanceMargin(securities["EURUSD"])); //Console.WriteLine("EURUSD Value: " + securities["EURUSD"].Holdings.HoldingsValue); //Console.WriteLine(); var config3 = CreateTradeBarDataConfig(SecurityType.Forex, "EURGBP"); securities.Add(new QuantConnect.Securities.Forex.Forex(SecurityExchangeHours, gbpCash, config3, 100)); securities["EURGBP"].Holdings.SetHoldings(1m, 1000); securities["EURGBP"].SetMarketPrice(new TradeBar { Time = time, Value = 1m }); //Console.WriteLine("EURGBP TMU: " + securities["EURGBP"].MarginModel.GetMaintenanceMargin(securities["EURGBP"])); //Console.WriteLine("EURGBP Value: " + securities["EURGBP"].Holdings.HoldingsValue); //Console.WriteLine(); //Console.WriteLine(portfolio.CashBook["USD"]); //Console.WriteLine(portfolio.CashBook["EUR"]); //Console.WriteLine(portfolio.CashBook["GBP"]); //Console.WriteLine("CashBook: " + portfolio.CashBook.TotalValueInAccountCurrency); //Console.WriteLine(); //Console.WriteLine("Total Margin Used: " + portfolio.TotalMarginUsed); //Console.WriteLine("Total Free Margin: " + portfolio.MarginRemaining); //Console.WriteLine("Total Portfolio Value: " + portfolio.TotalPortfolioValue); var acceptedOrder = new MarketOrder("AAPL", 101, DateTime.Now) {Price = 100}; var sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, acceptedOrder); Assert.IsTrue(sufficientCapital); var rejectedOrder = new MarketOrder("AAPL", 102, DateTime.Now) { Price = 100 }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, rejectedOrder); Assert.IsFalse(sufficientCapital); }
/// <summary> /// Check if there is sufficient capital to execute this order. /// </summary> /// <param name="portfolio">Our portfolio</param> /// <param name="order">Order we're checking</param> /// <returns>True if suficient capital.</returns> public bool GetSufficientCapitalForOrder(SecurityPortfolioManager portfolio, Order order) { if (Math.Abs(GetOrderRequiredBuyingPower(order)) > portfolio.GetBuyingPower(order.Symbol, order.Direction)) { //Log.Debug("Symbol: " + order.Symbol + " Direction: " + order.Direction.ToString() + " Quantity: " + order.Quantity); //Log.Debug("GetOrderRequiredBuyingPower(): " + Math.Abs(GetOrderRequiredBuyingPower(order)) + " PortfolioGetBuyingPower(): " + portfolio.GetBuyingPower(order.Symbol, order.Direction)); return false; } return true; }
private Security InitializeTest(DateTime reference, out SecurityPortfolioManager portfolio) { var security = new Security(SecurityExchangeHours.AlwaysOpen(TimeZones.NewYork), CreateTradeBarConfig(), new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency)); security.SetMarketPrice(new Tick { Value = 100 }); var timeKeeper = new TimeKeeper(reference); var securityManager = new SecurityManager(timeKeeper); securityManager.Add(security); var transactionManager = new SecurityTransactionManager(securityManager); portfolio = new SecurityPortfolioManager(securityManager, transactionManager); portfolio.SetCash("USD", 100 * 1000m, 1m); Assert.AreEqual(0, security.Holdings.Quantity); Assert.AreEqual(100*1000m, portfolio.CashBook[CashBook.AccountCurrency].Amount); return security; }
/// <summary> /// Given this portfolio and order, what would the final portfolio holdings be if it were filled. /// </summary> /// <param name="portfolio">Portfolio we're running</param> /// <param name="order">Order requested to process </param> /// <returns>decimal final holdings </returns> private decimal GetExpectedFinalHoldings(SecurityPortfolioManager portfolio, Order order) { decimal expectedFinalHoldings = 0; if (portfolio.TotalAbsoluteHoldings > 0) { foreach (Security company in Securities.Values) { if (order.Symbol == company.Symbol) { //If the same holding, we must check if its long or short. expectedFinalHoldings += Math.Abs(company.Holdings.HoldingValue + (order.Price * (decimal)order.Quantity)); //Log.Debug("HOLDINGS: " + company.Holdings.HoldingValue + " - " + "ORDER: (P: " + order.Price + " Q:" + order.Quantity + ") EXPECTED FINAL HOLDINGS: " + expectedFinalHoldings + " BUYING POWER: " + portfolio.GetBuyingPower(order.Symbol)); } else { //If not the same asset, then just add the absolute holding to the final total: expectedFinalHoldings += company.Holdings.AbsoluteHoldings; } } } else { //First purchase: just make calc abs order size: expectedFinalHoldings = (order.Price * (decimal)order.Quantity); } return expectedFinalHoldings; }
/// <summary> /// Validate the transOrderDirection is a sensible choice, factoring in basic limits. /// </summary> /// <param name="order">Order to Validate</param> /// <param name="portfolio">Security portfolio object we're working on.</param> /// <param name="time">Current actual time</param> /// <param name="maxOrders">Maximum orders per day/period before rejecting.</param> /// <param name="price">Current actual price of security</param> /// <returns>If negative its an error, or if 0 no problems.</returns> public int ValidateOrder(Order order, SecurityPortfolioManager portfolio, DateTime time, int maxOrders = 50, decimal price = 0) { //-1: Order quantity must not be zero if (order.Quantity == 0 || order.Direction == OrderDirection.Hold) return -1; //-2: There is no data yet for this security - please wait for data (market order price not available yet) //if (order.Price <= 0) return -2; // Not valid anymore with custom data - need to accept negative data. //-3: Attempting market order outside of market hours if (!Securities[order.Symbol].Exchange.ExchangeOpen && order.Type == OrderType.Market) return -3; //-4: Insufficient capital to execute order if (GetSufficientCapitalForOrder(portfolio, order) == false) return -4; //-5: Exceeded maximum allowed orders for one analysis period. if (Orders.Count > maxOrders) return -5; //-6: Order timestamp error. Order appears to be executing in the future if (order.Time > time) return -6; return 0; }
/// <summary> /// Update an order yet to be filled / stop / limit. /// </summary> /// <param name="order">Order to Update</param> /// <param name="portfolio"></param> /// <returns>id if the order we modified.</returns> public int UpdateOrder(Order order, SecurityPortfolioManager portfolio) { try { //Update the order from the behaviour int id = order.Id; order.Time = Securities[order.Symbol].Time; //Run through a list of prepurchase checks, if any are false stop the transaction int orderError = ValidateOrder(order, portfolio, order.Time, int.MaxValue, order.Price); if (orderError < 0) { return orderError; } if (_orders.ContainsKey(id)) { //-> If its already filled return false; can't be updated if (_orders[id].Status == OrderStatus.Filled || _orders[id].Status == OrderStatus.Canceled) { return -5; } else { //Flag the order to be resubmitted. order.Status = OrderStatus.Update; _orders[id] = order; //Send the order to transaction handler to be processed. OrderQueue.Enqueue(order); } } else { //-> Its not in the orders cache, shouldn't get here return -6; } } catch (Exception err) { Log.Error("Algorithm.Transactions.UpdateOrder(): " + err.Message); return -7; } return 0; }
public void SellingShortFromZeroAddsToCash() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(0); securities.Add("AAPL", new Security(SecurityExchangeHours, CreateTradeBarDataConfig(SecurityType.Equity, "AAPL"), 1)); var fill = new OrderEvent(1, "AAPL", DateTime.MinValue, OrderStatus.Filled, OrderDirection.Sell, 100, -100, 0); portfolio.ProcessFill(fill); Assert.AreEqual(100 * 100, portfolio.Cash); Assert.AreEqual(-100, securities["AAPL"].Holdings.Quantity); }
/// <summary> /// Check if there is sufficient capital to execute this order. /// </summary> /// <param name="portfolio">Our portfolio</param> /// <param name="order">Order we're checking</param> /// <returns>True if suficient capital.</returns> public bool GetSufficientCapitalForOrder(SecurityPortfolioManager portfolio, Order order) { //First simple check, when don't hold stock, this will always increase portfolio regardless of direction if (Math.Abs(GetOrderRequiredBuyingPower(order)) > portfolio.GetBuyingPower(order.Symbol, order.Direction)) { //Log.Debug("Symbol: " + order.Symbol + " Direction: " + order.Direction.ToString() + " Quantity: " + order.Quantity); //Log.Debug("GetOrderRequiredBuyingPower(): " + Math.Abs(GetOrderRequiredBuyingPower(order)) + " PortfolioGetBuyingPower(): " + portfolio.GetBuyingPower(order.Symbol, order.Direction)); return false; } else { return true; } }
/// <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)); }
/// <summary> /// Update an order yet to be filled / stop / limit. /// </summary> /// <param name="order">Order to Update</param> /// <param name="portfolio"></param> /// <returns>id if the order we modified.</returns> public int UpdateOrder(Order order, SecurityPortfolioManager portfolio) { try { //Update the order from the behaviour int id = order.Id; order.Time = Securities[order.Symbol].Time; //Validate order: if (order.Price == 0 || order.Quantity == 0) return -1; if (_orders.ContainsKey(id)) { //-> If its already filled return false; can't be updated if (_orders[id].Status == OrderStatus.Filled || _orders[id].Status == OrderStatus.Canceled) { return -5; } else { //Flag the order to be resubmitted. order.Status = OrderStatus.Update; _orders[id] = order; //Send the order to transaction handler to be processed. OrderQueue.Enqueue(order); } } else { //-> Its not in the orders cache, shouldn't get here return -6; } } catch (Exception err) { Log.Error("Algorithm.Transactions.UpdateOrder(): " + err.Message); return -7; } return 0; }
public void ComputeMarginProperlyAsSecurityPriceFluctuates() { const decimal leverage = 1m; const int quantity = (int) (1000*leverage); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var orderProcessor = new OrderProcessor(); transactions.SetOrderProcessor(orderProcessor); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].SetAmount(quantity); var config = CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL); securities.Add(new Security(SecurityExchangeHours, config, new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.AAPL]; security.SetLeverage(leverage); var time = DateTime.Now; const decimal buyPrice = 1m; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, buyPrice, buyPrice, buyPrice, buyPrice, 1)); var order = new MarketOrder(Symbols.AAPL, quantity, time) {Price = buyPrice}; var fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; orderProcessor.AddOrder(order); var request = new SubmitOrderRequest(OrderType.Market, security.Type, security.Symbol, order.Quantity, 0, 0, order.Time, null); request.SetOrderId(0); orderProcessor.AddTicket(new OrderTicket(null, request)); Assert.AreEqual(portfolio.CashBook["USD"].Amount, fill.FillPrice*fill.FillQuantity); portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var newOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) {Price = buyPrice}; bool sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); // now the stock doubles, so we should have margin remaining time = time.AddDays(1); const decimal highPrice = buyPrice * 2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, highPrice, highPrice, highPrice, highPrice, 1)); Assert.AreEqual(quantity, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity * 2, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var anotherOrder = new MarketOrder(Symbols.AAPL, 1, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, anotherOrder); Assert.IsTrue(sufficientCapital); // now the stock plummets, so we should have negative margin remaining time = time.AddDays(1); const decimal lowPrice = buyPrice/2; security.SetMarketPrice(new TradeBar(time, Symbols.AAPL, lowPrice, lowPrice, lowPrice, lowPrice, 1)); Assert.AreEqual(-quantity/2m, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity/2m, portfolio.TotalPortfolioValue); // this would not cause a margin call due to leverage = 1 bool issueMarginCallWarning; var marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); // now change the leverage and buy more and we'll get a margin call security.SetLeverage(leverage * 2); order = new MarketOrder(Symbols.AAPL, quantity, time) { Price = buyPrice }; fill = new OrderEvent(order, DateTime.UtcNow, 0) { FillPrice = buyPrice, FillQuantity = quantity }; portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.TotalPortfolioValue); marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.AreNotEqual(0, marginCallOrders.Count); Assert.AreEqual(-security.Holdings.Quantity, marginCallOrders[0].Quantity); // we bought twice Assert.GreaterOrEqual(-portfolio.MarginRemaining, security.Price * marginCallOrders[0].Quantity); }
/// <summary> /// Gets the margin cash available for a trade /// </summary> /// <param name="portfolio">The algorithm's portfolio</param> /// <param name="security">The security to be traded</param> /// <param name="direction">The direction of the trade</param> /// <returns>The margin available for the trade</returns> public virtual decimal GetMarginRemaining(SecurityPortfolioManager portfolio, Security security, OrderDirection direction) { var holdings = security.Holdings; if (direction == OrderDirection.Hold) { return portfolio.MarginRemaining; } //If the order is in the same direction as holdings, our remaining cash is our cash //In the opposite direction, our remaining cash is 2 x current value of assets + our cash if (holdings.IsLong) { switch (direction) { case OrderDirection.Buy: return portfolio.MarginRemaining; case OrderDirection.Sell: return security.MarginModel.GetMaintenanceMargin(security)*2 + portfolio.MarginRemaining; } } else if (holdings.IsShort) { switch (direction) { case OrderDirection.Buy: return security.MarginModel.GetMaintenanceMargin(security)*2 + portfolio.MarginRemaining; case OrderDirection.Sell: return portfolio.MarginRemaining; } } //No holdings, return cash return portfolio.MarginRemaining; }
public void SellingShortFromShortAddsToCash() { var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(0); securities.Add(Symbols.AAPL, new Security(SecurityExchangeHours, CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL), new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); securities[Symbols.AAPL].Holdings.SetHoldings(100, -100); var fill = new OrderEvent(1, Symbols.AAPL, DateTime.MinValue, OrderStatus.Filled, OrderDirection.Sell, 100, -100, 0); Assert.AreEqual(-100, securities[Symbols.AAPL].Holdings.Quantity); portfolio.ProcessFill(fill); Assert.AreEqual(100 * 100, portfolio.Cash); Assert.AreEqual(-200, securities[Symbols.AAPL].Holdings.Quantity); }
/// <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)); }
public void EquitySellAppliesSettlementCorrectly() { var securityExchangeHours = SecurityExchangeHoursTests.CreateUsEquitySecurityExchangeHours(); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.SetCash(1000); securities.Add(Symbols.AAPL, new QuantConnect.Securities.Equity.Equity(securityExchangeHours, CreateTradeBarDataConfig(SecurityType.Equity, Symbols.AAPL), new Cash(CashBook.AccountCurrency, 0, 1m), SymbolProperties.GetDefault(CashBook.AccountCurrency))); var security = securities[Symbols.AAPL]; security.SettlementModel = new DelayedSettlementModel(3, TimeSpan.FromHours(8)); Assert.AreEqual(0, security.Holdings.Quantity); Assert.AreEqual(1000, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); // Buy on Monday var timeUtc = new DateTime(2015, 10, 26, 15, 30, 0); var orderFee = security.FeeModel.GetOrderFee(security,new MarketOrder(Symbols.AAPL, 10, timeUtc)); var fill = new OrderEvent(1, Symbols.AAPL, timeUtc, OrderStatus.Filled, OrderDirection.Buy, 100, 10, orderFee); portfolio.ProcessFill(fill); Assert.AreEqual(10, security.Holdings.Quantity); Assert.AreEqual(-1, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); // Sell on Tuesday, cash unsettled timeUtc = timeUtc.AddDays(1); orderFee = security.FeeModel.GetOrderFee(security, new MarketOrder(Symbols.AAPL, 10, timeUtc)); fill = new OrderEvent(2, Symbols.AAPL, timeUtc, OrderStatus.Filled, OrderDirection.Sell, 100, -10, orderFee); portfolio.ProcessFill(fill); Assert.AreEqual(0, security.Holdings.Quantity); Assert.AreEqual(-2, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Thursday, still cash unsettled timeUtc = timeUtc.AddDays(2); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(-2, portfolio.Cash); Assert.AreEqual(1000, portfolio.UnsettledCash); // Friday at open, cash settled var marketOpen = securityExchangeHours.MarketHours[timeUtc.DayOfWeek].GetMarketOpen(TimeSpan.Zero, false); Assert.IsTrue(marketOpen.HasValue); timeUtc = timeUtc.AddDays(1).Date.Add(marketOpen.Value).ConvertToUtc(securityExchangeHours.TimeZone); portfolio.ScanForCashSettlement(timeUtc); Assert.AreEqual(998, portfolio.Cash); Assert.AreEqual(0, portfolio.UnsettledCash); }
public void ComputeMarginProperlyAsSecurityPriceFluctuates() { const decimal leverage = 1m; const int quantity = (int) (1000*leverage); var securities = new SecurityManager(TimeKeeper); var transactions = new SecurityTransactionManager(securities); var portfolio = new SecurityPortfolioManager(securities, transactions); portfolio.CashBook["USD"].Quantity = quantity; var config = CreateTradeBarDataConfig(SecurityType.Equity, "AAPL"); securities.Add(new Security(SecurityExchangeHours, config, leverage, false)); var time = DateTime.Now; const decimal buyPrice = 1m; var security = securities["AAPL"]; security.SetMarketPrice(new TradeBar(time, "AAPL", buyPrice, buyPrice, buyPrice, buyPrice, 1)); var order = new MarketOrder("AAPL", quantity, time) {Price = buyPrice}; var fill = new OrderEvent(order){FillPrice = buyPrice, FillQuantity = quantity}; Assert.AreEqual(portfolio.CashBook["USD"].Quantity, fill.FillPrice*fill.FillQuantity); portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var newOrder = new MarketOrder("AAPL", 1, time.AddSeconds(1)) {Price = buyPrice}; bool sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, newOrder); Assert.IsFalse(sufficientCapital); // now the stock doubles, so we should have margin remaining time = time.AddDays(1); const decimal highPrice = buyPrice * 2; security.SetMarketPrice(new TradeBar(time, "AAPL", highPrice, highPrice, highPrice, highPrice, 1)); Assert.AreEqual(quantity, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity * 2, portfolio.TotalPortfolioValue); // we shouldn't be able to place a trader var anotherOrder = new MarketOrder("AAPL", 1, time.AddSeconds(1)) { Price = highPrice }; sufficientCapital = transactions.GetSufficientCapitalForOrder(portfolio, anotherOrder); Assert.IsTrue(sufficientCapital); // now the stock plummets, so we should have negative margin remaining time = time.AddDays(1); const decimal lowPrice = buyPrice/2; security.SetMarketPrice(new TradeBar(time, "AAPL", lowPrice, lowPrice, lowPrice, lowPrice, 1)); Assert.AreEqual(-quantity/2m, portfolio.MarginRemaining); Assert.AreEqual(quantity, portfolio.TotalMarginUsed); Assert.AreEqual(quantity/2m, portfolio.TotalPortfolioValue); // this would not cause a margin call due to leverage = 1 bool issueMarginCallWarning; var marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.AreEqual(0, marginCallOrders.Count); // now change the leverage and buy more and we'll get a margin call security.SetLeverage(leverage * 2); order = new MarketOrder("AAPL", quantity, time) { Price = buyPrice }; fill = new OrderEvent(order) { FillPrice = buyPrice, FillQuantity = quantity }; portfolio.ProcessFill(fill); Assert.AreEqual(0, portfolio.TotalPortfolioValue); marginCallOrders = portfolio.ScanForMarginCall(out issueMarginCallWarning); Assert.AreNotEqual(0, marginCallOrders.Count); Assert.AreEqual(-security.Holdings.Quantity, marginCallOrders[0].Quantity); // we bought twice Assert.GreaterOrEqual(-portfolio.MarginRemaining, security.Price * marginCallOrders[0].Quantity); }
/// <summary> /// Applies cash settlement rules /// </summary> /// <param name="portfolio">The algorithm's portfolio</param> /// <param name="security">The fill's security</param> /// <param name="applicationTimeUtc">The fill time (in UTC)</param> /// <param name="currency">The currency symbol</param> /// <param name="amount">The amount of cash to apply</param> public void ApplyFunds(SecurityPortfolioManager portfolio, Security security, DateTime applicationTimeUtc, string currency, decimal amount) { portfolio.CashBook[currency].AddAmount(amount); }
/// <summary> /// Initializes a new instance of the <see cref="MarginCallModel"/> class /// </summary> /// <param name="portfolio">The portfolio object to receive margin calls</param> public MarginCallModel(SecurityPortfolioManager portfolio) { Portfolio = portfolio; }
/// <summary> /// Check if there is sufficient capital to execute this order. /// </summary> /// <param name="portfolio">Our portfolio</param> /// <param name="order">Order we're checking</param> /// <returns>True if suficient capital.</returns> public bool GetSufficientCapitalForOrder(SecurityPortfolioManager portfolio, Order order) { // short circuit the div 0 case if (order.Quantity == 0) return true; var security = _securities[order.Symbol]; var ticket = GetOrderTicket(order.Id); if (ticket == null) { Log.Error("SecurityTransactionManager.GetSufficientCapitalForOrder(): Null order ticket for id: " + order.Id); return false; } var freeMargin = security.MarginModel.GetMarginRemaining(portfolio, security, order.Direction); var initialMarginRequiredForOrder = security.MarginModel.GetInitialMarginRequiredForOrder(security, order); // pro-rate the initial margin required for order based on how much has already been filled var percentUnfilled = (Math.Abs(order.Quantity) - Math.Abs(ticket.QuantityFilled))/Math.Abs(order.Quantity); var initialMarginRequiredForRemainderOfOrder = percentUnfilled*initialMarginRequiredForOrder; if (Math.Abs(initialMarginRequiredForRemainderOfOrder) > freeMargin) { Log.Error(string.Format("SecurityTransactionManager.GetSufficientCapitalForOrder(): Id: {0}, Initial Margin: {1}, Free Margin: {2}", order.Id, initialMarginRequiredForOrder, freeMargin)); return false; } return true; }
/// <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) { // 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.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; 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.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("ForexPortfolioManager.ProcessFill(orderEvent): " + err.Message); } //Set the results back to the vehicle. security.Holdings.SetHoldings(averageHoldingsPrice, Convert.ToInt32(quantityHoldings)); }