/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using NUnit.Framework;
using QuantConnect.Algorithm;
using QuantConnect.Data.Market;
using QuantConnect.Securities;
using QuantConnect.Brokerages;
using Moq;
using QuantConnect.Interfaces;
using QuantConnect.Lean.Engine.TransactionHandlers;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Tests.Common.Securities;
using QuantConnect.Tests.Engine.DataFeeds;
using System.Linq;
using QuantConnect.Data;
using QuantConnect.Indicators;
using Python.Runtime;
using QuantConnect.Algorithm.Framework.Portfolio;
namespace QuantConnect.Tests.Algorithm
{
[TestFixture, Parallelizable(ParallelScope.Fixtures)]
public class AlgorithmTradingTests
{
private static FakeOrderProcessor _fakeOrderProcessor;
private static TestCaseData[] TestParameters
{
get
{
return new[]
{
new TestCaseData(1m),
new TestCaseData(2m),
new TestCaseData(100m),
};
}
}
private static TestCaseData[] TestParametersDifferentMargins
{
get
{
return new[]
{
new TestCaseData(0.5m, 0.25m),
};
}
}
/*****************************************************/
// Isostatic market conditions tests.
/*****************************************************/
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ZeroToLong(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25 & Target 50%
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
Assert.AreEqual(1995m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ZeroToLong_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25 & Target 50%
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// $100k total value * 0.5 target * 0.9975 FreePortfolioValuePercentage / 25 ~= 1995 - fees
Assert.AreEqual(1994m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ZeroToLong_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25 & Target 50%
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// ($100k total value - 10 k fees) * 0.5 target * 0.9975 FreePortfolioValuePercentage / 25 ~= 1795m
Assert.AreEqual(1795m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ZeroToShort(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25 & Target 50%
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-1995m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ZeroToShort_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25 & Target 50%
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-1994m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ZeroToShort_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25 & Target 50%
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
// ($100k total value - 10 k fees) * -0.5 target * 0.9975 FreePortfolioValuePercentage / 25 ~= -1795m
Assert.AreEqual(-1795m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToLonger(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Calculate the new holdings:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.75m);
Assert.AreEqual(992m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToLonger_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Calculate the new holdings:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.75m);
Assert.AreEqual(992m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToLonger_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Calculate the new holdings:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.75m);
Assert.AreEqual(693m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongerToLong(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//75% cash spent on 3000 MSFT shares.
algo.Portfolio.SetCash(25000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 3000);
// TPV = Cash + Holdings - Fees - Buffer => Target = TVP * 0.5
// TPV = 25000 + 25 * 3000 - 0 - 250 = 99,750 => 99,750 * 0.5 = 49875
// Final Quantity = Target / Unit - Holdings Quantity
// Final Quantity = 49875 / 25 - 3000 = 1995 - 3000 = -1,005
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// 3000 - 1005 = 1995. Multiply by unit 1995 * 25 = 49,875. Weight = 49,875 / 99,750 (TPV) = 0.5
Assert.AreEqual(-1005m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
///
/// Reproduce QC Slack Issue https://quantconnect.slack.com/archives/G51920EN4/p1625782914057900
/// Original Algorithm: https://www.quantconnect.com/terminal/processCache?request=embedded_backtest_e35c58ed9304f452bb43c4fbf76fe153.html
///
/// Test to see that in the event of a precision error we still adjust the quantity to reach our target
///
[Test]
public void PrecisionFailureAdjustment()
{
Security msft;
var algo = GetAlgorithm(out msft, 2, 0);
Update(msft, 66.5m);
algo.Portfolio.SetCash(112302.5m);
algo.Settings.FreePortfolioValue = 0;
algo.Portfolio[Symbols.MSFT].SetHoldings(66.5m, -190);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.4987458298843655153385005142m * 2);
Assert.AreEqual(1684, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongerToLong_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//75% cash spent on 3000 MSFT shares.
algo.Portfolio.SetCash(25000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 3000);
// TPV = Cash + Holdings - Fees - Buffer => Target = TVP * 0.5
// TPV = 25000 + 25 * 3000 - 1 - 250 = 99,749 => 99,749 * 0.5 = 49874.5
// Final Quantity = Target / Unit - Holdings Quantity
// Final Quantity = 49874.5 / 25 - 3000 = 1794.98 - 3000 = -1,005.02 -> -1006
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// 3000 - 1006 = 1994. Multiply by unit 1994 * 25 = 49,850. Weight = 49,875 / 99,749 (TPV) = 0.49975 < 0.5
Assert.AreEqual(-1006m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongerToLong_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//75% cash spent on 3000 MSFT shares.
algo.Portfolio.SetCash(25000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 3000);
// TPV = Cash + Holdings - Fees - Buffer => Target = TVP * 0.5
// TPV = 25000 + 25 * 3000 - 10000 - 250 = 89750 => 89750 * 0.5 = 44875
// Final Quantity = Target / Unit - Holdings Quantity
// Final Quantity = 44875 / 25 - 3000 = 1795.0 - 3000 = -1205
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// 3000 - 1205 = 1795. Multiply by unit 1795 * 25 = 44875. Weight = 44875 / 89750 (TPV) = 0.5
Assert.AreEqual(-1205m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToZero(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0m);
Assert.AreEqual(-2000, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToZero_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0m);
Assert.AreEqual(-2000, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToZero_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0m);
Assert.AreEqual(-2000, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToShort(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Need to sell to make position ($100k total value * -0.5 target * 0.9975 buffer - $50k current holdings) / 50 =~ -3995m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-3995m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToShort_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Need to sell to make position ($100k total value * -0.5 target * 0.9975 buffer - $50k current holdings) / 50 =~ -3995m - 1 due to fee
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-3994m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToShort_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Need to sell to make position (($100k total value - 10 K)* -0.5 target * 0.9975 buffer - $50k current holdings) / 25 =~ -3795m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-3795m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_HalfLongToFullShort(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Need to sell to make position ($100k total value * -1 target * 0.9975 buffer - $50k current holdings) / 50 =~ -5990m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -1m);
Assert.AreEqual(-5990m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_HalfLongToFullShort_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Need to sell to make position ($100k total value * -1 target * 0.9975 buffer - $50k current holdings) / 50 =~ -5990m - 1 due to fee
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -1m);
Assert.AreEqual(-5989m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_HalfLongToFullShort_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
// Fee is 10k / 25 ~= 400 shares
// Need to sell to make position (($100k total value - 10k fees) * -1 target * 0.9975 buffer - $50k current holdings) / 25 =~ -5591m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -1m);
Assert.AreEqual(-5591m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToZero(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
//Buy 2000 to get to 0 holdings.
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0m);
Assert.AreEqual(2000, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToZero_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
//Buy 2000 to get to 0 holdings.
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0m);
Assert.AreEqual(2000, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToZero_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
//Buy 2000 to get to 0 holdings.
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0m);
Assert.AreEqual(2000, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToShorter(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// Cash: 150k
// MSFT: -50k
// TPV: 100k
// we should end with -3000 = -.75*(100k/25)
// ($100k total value * -0.75 target * 0.9975 buffer - $50k current holdings) / 25 =~ 992m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.75m);
Assert.AreEqual(-992m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToShorter_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// Cash: 150k
// MSFT: -50k
// TPV: 100k
// we should end with -3000 = -.75*(100k/25)
// ($100k total value * -0.75 target * 0.9975 buffer - $50k current holdings) / 25 =~ 992m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.75m);
Assert.AreEqual(-992m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToShorter_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// Cash: 150k
// MSFT: -50k
// TPV: 100k
// we should end with -3000 = -.75*(100k/25)
// (($100k total value - 10k fees) * -0.75 target * 0.9975 buffer + $50k current holdings) / 25 =~ -693m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.75m);
Assert.AreEqual(-693m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToLong(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// ($100k total value * 0.5 target * 0.9975 buffer - $50k current holdings) / 25 =~ 3995m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
Assert.AreEqual(3995m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToLong_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// ($100k total value * 0.5 target * 0.9975 buffer - $50k current holdings) / 25 =~ 3995m - 1 cause order fee
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
Assert.AreEqual(3994m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToLong_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// (($100k total value - 10 k fees) * 0.5 target * 0.9975 buffer - $50k current holdings) / 25 =~ 3995m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
Assert.AreEqual(3795m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToHalfShort_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held + -2000 to get to -50%
// ($100k total value * -0.5 target * 0.9975 buffer - $50k current holdings) / 25 =~ 3995m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-3995m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToHalfShort_SmallConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held + -1999 to get to -50%
// ($100k total value * -0.5 target * 0.9975 buffer - $50k current holdings) / 25 =~ 3995m - 1 due to fees
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-3994m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToHalfShort_HighConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held + -1600 to get to -50%
// ($100k total value * -0.5 target * 0.9975 buffer - $50k current holdings) / 25 =~ 3995m - 200 due to fees
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-3795m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToFullShort_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Need to sell to make position ($100k total value * -1 target * 0.9975 buffer - $50k current holdings) / 50 =~ -5990m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -1m);
Assert.AreEqual(-5990m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToFullShort_SmallConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Need to sell to make position ($100k total value * -1 target * 0.9975 buffer - $50k current holdings) / 50 =~ -5990m - 1 due to fee
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -1m);
Assert.AreEqual(-5989m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToFullShort_HighConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
// Fee is 10k / 25 ~= 400 shares
//Need to sell to make position (($100k total value -10k fees) * -1 target * 0.9975 buffer - $50k current holdings) / 50 =~ -5591m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -1m);
Assert.AreEqual(-5591m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToFull2xShort_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held + -8000 to get to -200%
// ($100k total value * -2 target * 0.9975 buffer - $50k current holdings) / 25 =~ 9980m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -2m);
Assert.AreEqual(-9980m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToFull2xShort_SmallConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held + -7999 to get to -200%
// ($100k total value * -2 target * 0.9975 buffer - $50k current holdings) / 25 =~ 9980m - 1 due to fees
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -2m);
Assert.AreEqual(-9979m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_HalfLongToFull2xShort_HighConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Sell all 2000 held + -7200 to get to -200%
// ($100k total value * -2 target * 0.9975 buffer - $50k current holdings) / 25 =~ 9980m - ~800 due to fees
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -2m);
Assert.AreEqual(-9182m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_ZeroToFullShort_SmallConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 10000);
//Set price to $25
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -2m);
// ($100k total value * -2 target * 0.9975 buffer - $10k fees * 2) / 25 =~-7182m
Assert.AreEqual(-7182m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_ZeroToAlmostFullShort_SmallConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 10000);
//Set price to $25
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -1.5m);
// ($100k total value * -1.5 target * 0.9975 buffer - $10k fees * 1.5) / 25 =~ -5386m
Assert.AreEqual(-5386m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_ZeroToFullLong_SmallConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 10000);
//Set price to $25
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 2m);
// ($100k total value * 2 target * 0.9975 buffer - $10k fees * 2) / 25 =~ 7182m
Assert.AreEqual(7182m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParametersDifferentMargins))]
public void SetHoldings_ZeroToAlmostFullLong_SmallConstantFeeStructure_DifferentMargins(decimal initialMarginRequirement, decimal maintenanceMarginRequirement)
{
Security msft;
var algo = GetAlgorithm(out msft, initialMarginRequirement, maintenanceMarginRequirement, 10000);
//Set price to $25
Update(msft, 25);
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 1.5m);
// ($100k total value * 1.5 target * 0.9975 buffer - $10k fees * 1.5) / 25 =~ 5386m
Assert.AreEqual(5386m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
/*****************************************************/
// Rising market conditions tests.
/*****************************************************/
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongFixed_PriceRise(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k.
//Calculate the new holdings for 50% MSFT::
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// Need to sell ($150k total value * 0.5m target * 0.9975 buffer - 100k current holdings) / 50 = -503.75 > ~-504
Assert.AreEqual(-504m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongFixed_PriceRise_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k.
//Calculate the new holdings for 50% MSFT::
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// Need to sell ($150k total value * 0.5m target * 0.9975 buffer - 100k current holdings) / 50 =~ -503.75 > ~504
Assert.AreEqual(-504m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongFixed_PriceRise_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k.
//Calculate the new holdings for 50% MSFT::
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// Need to sell (( $150k total value - 10 k fees) * 0.5m target * 0.9975 buffer - 100k current holdings) / 50 =~ -603.5 > -604
Assert.AreEqual(-604, actual);
// After the trade: TPV 140k (due to fees), holdings at 1397 shares (2000 - 603) * $50 = 69850 value, which is 0.4989% holdings
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToLonger_PriceRise(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k. MSFT is already 66% of holdings.
//Calculate the order for 75% MSFT:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.75m);
//Need to buy to make position ($150k total value * 0.75 target * 0.9975 buffer - 100k current holdings) / 50 =~ 244
Assert.AreEqual(244m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToLonger_PriceRise_SmallConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 1);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k. MSFT is already 66% of holdings.
//Calculate the order for 75% MSFT:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.75m);
//Need to buy to make position (150K total value * 0.75 target * 0.9975 buffer - 100k current holdings) / 50 =~ 244
Assert.AreEqual(244m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToLonger_PriceRise_HighConstantFeeStructure(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 10000);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k. MSFT is already 66% of holdings.
//Calculate the order for 75% MSFT:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.75m);
//Need to buy to make position ((150K total value - 10k fees) * 0.75 target * 0.9975 buffer - 100k current holdings) / 50 =~ 94
Assert.AreEqual(94m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongerToLong_PriceRise(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//75% cash spent on 3000 MSFT shares.
algo.Portfolio.SetCash(25000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 3000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 3000 * 50 = $150k Holdings, $25k Cash: $175k. MSFT is 86% of holdings.
//Calculate the order for 50% MSFT:
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
//Need to sell to make position ($175k total value * 0.5 target * 0.9975 buffer - $150k current holdings) / 50 =~ -1255m
Assert.AreEqual(-1255m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_LongToShort_PriceRise(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Half cash spent on 2000 MSFT shares.
algo.Portfolio.SetCash(50000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k. MSFT is 66% of holdings.
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
//Need to sell to make position ($150k total value * -0.5 target * 0.9975 buffer - $100k current holdings) / 50 =~ -3496m
Assert.AreEqual(-3496m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToShorter_PriceRise(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
//Price rises to $50.
Update(msft, 50);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
// Cash: 150k
// MSFT: -(2000*50) = -100K
// TPV: 50k
Assert.AreEqual(50000, algo.Portfolio.TotalPortfolioValue);
// we should end with -748 shares (-.75*(50000-125)/50)
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.75m);
// currently -2000, so plus 1252
Assert.AreEqual(1252m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToLong_PriceRise_ZeroValue(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
//Price rises to $50: holdings now worthless.
Update(msft, 50m);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
//Now: 2000 * 50 = $0k Net Holdings, $50k Cash: $50k. MSFT is 0% of holdings.
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
//We want to be 50% long, this is currently +2000 holdings + 50% 50k = $25k * 0.9975 buffer/ $50-share~=2498m
Assert.AreEqual(2498m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortToLong_PriceRise(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
//Price rises to $50
Update(msft, 50m);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
// Cash: 150k
// MSFT: -50*2000=100k
// TPV: 50k
Assert.AreEqual(50000, algo.Portfolio.TotalPortfolioValue);
// 50k*0.5=25k = 500 end holdings
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// ($50k total value * 0.5 target * 0.9975 buffer - (-$100k current holdings)) / 50 =~ 2498m
Assert.AreEqual(2498m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
/*****************************************************/
// Falling market conditions tests.
/*****************************************************/
[Test, TestCaseSource(nameof(TestParameters))]
public void SetHoldings_ShortFixed_PriceFall(decimal leverage)
{
Security msft;
var algo = GetAlgorithm(out msft, leverage, 0);
//Set price to $25
Update(msft, 25);
//Sold -2000 MSFT shares, +50k cash
algo.Portfolio.SetCash(150000);
algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
Update(msft, 12.5m);
algo.Settings.FreePortfolioValue =
algo.Portfolio.TotalPortfolioValue * algo.Settings.FreePortfolioValuePercentage;
// Cash: 150k
// MSFT: -25k
// TPV : 125k
// ($125k total value * -0.5 target * 0.9975 buffer - (-$25k current holdings)) / 12.5 =~ -2987m
var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
Assert.AreEqual(-2987m, actual);
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, msft, algo));
}
/*************************************************************************/
// Rounding the order quantity to the nearest multiple of lot size test
/*************************************************************************/
[Test]
public void SetHoldings_Long_RoundOff()
{
var algo = new QCAlgorithm();
algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo));
var symbol = algo.AddForex("EURUSD", market: Market.FXCM).Symbol;
algo.SetCash(100000);
algo.SetCash("BTC", 0, 8000);
algo.SetBrokerageModel(BrokerageName.FxcmBrokerage);
algo.Securities[symbol].FeeModel = new ConstantFeeModel(0);
Security eurusd = algo.Securities[symbol];
// Set Price to $26
Update(eurusd, 26);
// So 100000/26 = 3846, After Rounding off becomes 3000
var actual = algo.CalculateOrderQuantity(symbol, 1m);
Assert.AreEqual(3000m, actual);
var btcusd = algo.AddCrypto("BTCUSD", market: Market.Coinbase);
btcusd.FeeModel = new ConstantFeeModel(0);
// Set Price to $26
Update(btcusd, 26);
// (100000 * 0.9975) / 26 = 3836.53846153m
actual = algo.CalculateOrderQuantity(Symbols.BTCUSD, 1m);
Assert.AreEqual(3836.53846153m, actual);
}
[Test]
public void SetHoldings_Short_RoundOff()
{
var algo = new QCAlgorithm();
algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo));
var symbol = algo.AddForex("EURUSD", market: Market.FXCM).Symbol;
algo.SetCash(100000);
algo.SetBrokerageModel(BrokerageName.FxcmBrokerage);
algo.Securities[symbol].FeeModel = new ConstantFeeModel(0);
Security eurusd = algo.Securities[symbol];
// Set Price to $26
Update(eurusd, 26);
// So -100000/26 = -3846, After Rounding off becomes -3000
var actual = algo.CalculateOrderQuantity(symbol, -1m);
Assert.AreEqual(-3000m, actual);
var btcusd = algo.AddCrypto("BTCUSD", market: Market.Coinbase);
btcusd.BuyingPowerModel = new CashBuyingPowerModel();
btcusd.FeeModel = new ConstantFeeModel(0);
// Set Price to $26
Update(btcusd, 26);
// Cash model does not allow shorts
actual = algo.CalculateOrderQuantity(Symbols.BTCUSD, -1m);
Assert.AreEqual(0, actual);
}
[Test]
public void SetHoldings_Long_ToZero_RoundOff()
{
var algo = new QCAlgorithm();
algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo));
var symbol = algo.AddForex("EURUSD", market: Market.FXCM).Symbol;
algo.SetCash(10000);
algo.SetBrokerageModel(BrokerageName.FxcmBrokerage);
algo.Securities[symbol].FeeModel = new ConstantFeeModel(0);
Security eurusd = algo.Securities[symbol];
// Set Price to $25
Update(eurusd, 25);
// So 10000/25 = 400, After Rounding off becomes 0
var actual = algo.CalculateOrderQuantity(symbol, 1m);
Assert.AreEqual(0m, actual);
}
//[Test]
//public void SetHoldings_LongToLonger_PriceRise()
//{
// var algo = GetAlgorithm();
// //Set price to $25
// Update(msft, 25));
// //Half cash spent on 2000 MSFT shares.
// algo.Portfolio.SetCash(50000);
// algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
// //Price rises to $50.
// Update(msft, 50));
// //Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k. MSFT is already 66% of holdings.
// //Calculate the order for 75% MSFT:
// var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.75m);
// //Need to buy to make position $112.5k == $12.5k / 50 = 250 shares
// Assert.AreEqual(250, actual);
//}
//[Test]
//public void SetHoldings_LongerToLong_PriceRise()
//{
// var algo = GetAlgorithm();
// //Set price to $25
// Update(msft, 25));
// //75% cash spent on 3000 MSFT shares.
// algo.Portfolio.SetCash(25000);
// algo.Portfolio[Symbols.MSFT].SetHoldings(25, 3000);
// //Price rises to $50.
// Update(msft, 50));
// //Now: 3000 * 50 = $150k Holdings, $25k Cash: $175k. MSFT is 86% of holdings.
// //Calculate the order for 50% MSFT:
// var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// //Need to sell to 50% = 87.5k target from $150k = 62.5 / $50-share = 1250
// Assert.AreEqual(-1250, actual);
//}
//[Test]
//public void SetHoldings_LongToShort_PriceRise()
//{
// var algo = GetAlgorithm();
// //Set price to $25
// Update(msft, 25));
// //Half cash spent on 2000 MSFT shares.
// algo.Portfolio.SetCash(50000);
// algo.Portfolio[Symbols.MSFT].SetHoldings(25, 2000);
// //Price rises to $50.
// Update(msft, 50));
// //Now: 2000 * 50 = $100k Holdings, $50k Cash: $150k. MSFT is 66% of holdings.
// var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.5m);
// // Need to hold -75k from $100k = delta: $175k / $50-share = -3500 shares.
// Assert.AreEqual(-3500, actual);
//}
//[Test]
//public void SetHoldings_ShortToShorter_PriceRise()
//{
// var algo = GetAlgorithm();
// //Set price to $25
// Update(msft, 25));
// //Half cash spent on -2000 MSFT shares.
// algo.Portfolio.SetCash(50000);
// algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// //Price rises to $50.
// Update(msft, 50));
// //Now: 2000 * 50 = $0k Net Holdings, $50k Cash: $50k. MSFT is 0% of holdings.
// var actual = algo.CalculateOrderQuantity(Symbols.MSFT, -0.75m);
// //Want to hold -75% of MSFT: 50k total, -37.5k / $50-share = -750 TOTAL.
// // Currently -2000, so net order +1250.
// Assert.AreEqual(1250, actual);
//}
//[Test]
//public void SetHoldings_ShortToLong_PriceRise()
//{
// var algo = GetAlgorithm();
// //Set price to $25
// Update(msft, 25));
// //Half cash spent on -2000 MSFT shares.
// algo.Portfolio.SetCash(50000);
// algo.Portfolio[Symbols.MSFT].SetHoldings(25, -2000);
// //Price rises to $50.
// Update(msft, 50));
// //Now: 2000 * 50 = $0k Net Holdings, $50k Cash: $50k. MSFT is 0% of holdings.
// var actual = algo.CalculateOrderQuantity(Symbols.MSFT, 0.5m);
// //We want to be 50% long, this is currently +2000 holdings + 50% 50k = $25k/ $50-share=500
// Assert.AreEqual(2500, actual);
//}
[TestCaseSource(nameof(SetHoldingReturnsOrderTicketsTestCases))]
public void SetHoldingsReturnsOrderTicketsTest(List symbols, bool liquidateExistingHoldings, Dictionary expectedOrders, string tag)
{
// Initialize the algorithm and add equities to the portfolio
var algo = GetAlgorithm(out _, 1, 0);
var appl = algo.AddEquity("AAPL");
var spy = algo.AddEquity("SPY");
var ibm = algo.AddEquity("IBM");
// Update prices and set initial holdings for the equities
Update(appl, 100);
Update(spy, 200);
Update(ibm, 300);
appl.Holdings.SetHoldings(25, 3);
spy.Holdings.SetHoldings(25, 3);
ibm.Holdings.SetHoldings(25, 3);
List orderTickets;
if (symbols.Count > 1)
{
// Handle multiple symbols by creating portfolio targets
var portfolioTargets = new List();
foreach (var symbol in symbols)
{
portfolioTargets.Add(new PortfolioTarget(symbol, 0.5m));
}
orderTickets = algo.SetHoldings(portfolioTargets, liquidateExistingHoldings, tag);
}
else
{
// Handle a single symbol or no symbols
if (symbols.Count != 0)
{
orderTickets = algo.SetHoldings(symbols.First(), 1, liquidateExistingHoldings, tag);
}
else
{
orderTickets = algo.SetHoldings(new List(), liquidateExistingHoldings, tag);
}
}
// Assert that the number of tickets matches the expected count
Assert.AreEqual(expectedOrders.Count, orderTickets.Count);
// Check each ticket:
// 1. Ensure the symbol is in the expectedOrders dictionary.
// 2. Verify the quantity matches the expected value for that symbol.
foreach (var ticket in orderTickets)
{
Assert.IsTrue(expectedOrders.ContainsKey(ticket.Symbol));
Assert.AreEqual(expectedOrders[ticket.Symbol], ticket.Quantity);
}
}
[Test]
public void OrderQuantityConversionTest()
{
Security msft;
var algo = GetAlgorithm(out msft, 1, 0);
//Set price to $25
Update(msft, 25);
algo.Portfolio.SetCash(150000);
var mock = new Mock();
var request = new Mock(null, null, null, null, null, null, null, null, null, null);
mock.Setup(m => m.Process(It.IsAny())).Returns(new OrderTicket(null, request.Object));
mock.Setup(m => m.GetOpenOrders(It.IsAny>())).Returns(new List());
algo.Transactions.SetOrderProcessor(mock.Object);
algo.Buy(Symbols.MSFT, 1);
algo.Buy(Symbols.MSFT, 1.0);
algo.Buy(Symbols.MSFT, 1.0m);
algo.Buy(Symbols.MSFT, 1.0f);
algo.Sell(Symbols.MSFT, 1);
algo.Sell(Symbols.MSFT, 1.0);
algo.Sell(Symbols.MSFT, 1.0m);
algo.Sell(Symbols.MSFT, 1.0f);
algo.Order(Symbols.MSFT, 1);
algo.Order(Symbols.MSFT, 1.0);
algo.Order(Symbols.MSFT, 1.0m);
algo.Order(Symbols.MSFT, 1.0f);
algo.MarketOrder(Symbols.MSFT, 1);
algo.MarketOrder(Symbols.MSFT, 1.0);
algo.MarketOrder(Symbols.MSFT, 1.0m);
algo.MarketOrder(Symbols.MSFT, 1.0f);
algo.MarketOnOpenOrder(Symbols.MSFT, 1);
algo.MarketOnOpenOrder(Symbols.MSFT, 1.0);
algo.MarketOnOpenOrder(Symbols.MSFT, 1.0m);
algo.MarketOnCloseOrder(Symbols.MSFT, 1);
algo.MarketOnCloseOrder(Symbols.MSFT, 1.0);
algo.MarketOnCloseOrder(Symbols.MSFT, 1.0m);
algo.LimitOrder(Symbols.MSFT, 1, 1);
algo.LimitOrder(Symbols.MSFT, 1.0, 1);
algo.LimitOrder(Symbols.MSFT, 1.0m, 1);
algo.StopMarketOrder(Symbols.MSFT, 1, 1);
algo.StopMarketOrder(Symbols.MSFT, 1.0, 1);
algo.StopMarketOrder(Symbols.MSFT, 1.0m, 1);
algo.StopLimitOrder(Symbols.MSFT, 1, 1, 2);
algo.StopLimitOrder(Symbols.MSFT, 1.0, 1, 2);
algo.StopLimitOrder(Symbols.MSFT, 1.0m, 1, 2);
algo.TrailingStopOrder(Symbols.MSFT, 1, 1, true);
algo.TrailingStopOrder(Symbols.MSFT, 1.0, 1, true);
algo.TrailingStopOrder(Symbols.MSFT, 1.0m, 1, true);
algo.TrailingStopOrder(Symbols.MSFT, 1, 1, 0.01m, false);
algo.TrailingStopOrder(Symbols.MSFT, 1.0, 1, 0.01m, false);
algo.TrailingStopOrder(Symbols.MSFT, 1.0m, 1, 0.01m, false);
algo.LimitIfTouchedOrder(Symbols.MSFT, 1, 1, 2);
algo.LimitIfTouchedOrder(Symbols.MSFT, 1.0, 1, 2);
algo.LimitIfTouchedOrder(Symbols.MSFT, 1.0m, 1, 2);
algo.SetHoldings(Symbols.MSFT, 1);
algo.SetHoldings(Symbols.MSFT, 1.0);
algo.SetHoldings(Symbols.MSFT, 1.0m);
algo.SetHoldings(Symbols.MSFT, 1.0f);
const int expected = 44;
Assert.AreEqual(expected, algo.Transactions.LastOrderId);
}
[TestCaseSource(nameof(LiquidateWorksAsExpectedTestCases))]
public void LiquidateWorksAsExpected(Language language, bool? multipleSymbols, bool isAsynchronous, TimeInForce timeInForce)
{
Security msft;
var algo = GetAlgorithm(out msft, 1, 0);
algo.AddEquity("AAPL");
algo.Securities[Symbols.AAPL].FeeModel = new ConstantFeeModel(0);
var aapl = algo.Securities[Symbols.AAPL];
aapl.SetLeverage(1);
msft.Holdings.SetHoldings(25, 3);
aapl.Holdings.SetHoldings(25, 7);
msft.Exchange.SetMarketHours(new List() { MarketHoursSegment.OpenAllDay() });
aapl.Exchange.SetMarketHours(new List() { MarketHoursSegment.OpenAllDay() });
//Set price to $25
Update(msft, 25);
Update(aapl, 25);
algo.Portfolio.SetCash(15000000);
var limitOrderCanceled = false;
// Setup the transaction handler
var tickets = new Dictionary();
var mock = new Mock();
var request = new Mock(null, null, null, null, null, null, null, null, null, null);
mock.Setup(m => m.Process(It.IsAny())).Returns(s =>
{
if (s.OrderRequestType == OrderRequestType.Cancel)
{
limitOrderCanceled = true;
return new OrderTicket(null, request.Object);
}
var orderRequest = s as SubmitOrderRequest;
var submitOrderRequest = new SubmitOrderRequest(orderRequest.OrderType, SecurityType.Equity, orderRequest.Symbol, orderRequest.Quantity, 0, 0, DateTime.UtcNow, "", orderRequest.OrderProperties);
submitOrderRequest.SetOrderId((int)orderRequest.Quantity);
var ticket = new OrderTicket(null, submitOrderRequest);
tickets[ticket.OrderId] = ticket;
return ticket;
});
algo.Transactions.SetOrderProcessor(mock.Object);
// Make the initial orders
var order1 = algo.MarketOrder(Symbols.MSFT, 1);
var order2 = algo.MarketOrder(Symbols.MSFT, 2);
var order3 = algo.MarketOrder(Symbols.AAPL, 3);
var order4 = algo.MarketOrder(Symbols.AAPL, 4);
var order5 = algo.LimitOrder(Symbols.AAPL, 5, 10);
// Setup the transaction handler to get the open orders as well as the order tickets
mock.Setup(m => m.GetOpenOrders(It.IsAny>())).Returns>(filter => tickets.Values.Select(x => Order.CreateOrder(x.SubmitRequest)).Where(x => filter(x)).ToList());
mock.Setup(m => m.GetOrderTicket(It.IsAny())).Returns(s =>
{
if (s < 0 && isAsynchronous)
{
// This means that the method `Transactions.WaitForOrder()` was called, since the
// negative ID's, in these case, come from the cancel orders, this is, from the
// liquidate method
throw new RegressionTestException("The orders were supposed to be liquidated asynchronously, but instead" +
"they were liquidated synchronously");
}
else
{
return tickets[s];
}
});
// Test different Liquidate() constructors
var orderProperties = timeInForce != null ? new OrderProperties() { TimeInForce = timeInForce } : null;
List liquidatedTickets;
if (language == Language.CSharp)
{
if (multipleSymbols == true)
{
liquidatedTickets = algo.Liquidate(new List() { Symbols.AAPL, Symbols.MSFT }, asynchronous: isAsynchronous, orderProperties: orderProperties);
}
else if (multipleSymbols == false)
{
liquidatedTickets = algo.Liquidate(Symbols.AAPL, asynchronous: isAsynchronous, orderProperties: orderProperties);
liquidatedTickets.AddRange(algo.Liquidate(Symbols.MSFT, asynchronous: isAsynchronous, orderProperties: orderProperties));
}
else
{
liquidatedTickets = algo.Liquidate(asynchronous: isAsynchronous, orderProperties: orderProperties);
}
}
else
{
using (Py.GIL())
{
liquidatedTickets = algo.Liquidate((new List() { Symbols.AAPL, Symbols.MSFT }).ToPython(), asynchronous: isAsynchronous, orderProperties: orderProperties);
}
}
// Assert the symbols were liquidated asynchronously
var aaplTicket = liquidatedTickets.Where(x => x.Symbol == Symbols.AAPL).Single();
var msftTicket = liquidatedTickets.Where(x => x.Symbol == Symbols.MSFT).Single();
Assert.AreEqual(aapl.Holdings.Quantity * (-2), aaplTicket.Quantity);
Assert.AreEqual(msft.Holdings.Quantity * (-2), msftTicket.Quantity);
Assert.AreEqual(timeInForce ?? TimeInForce.GoodTilCanceled, aaplTicket.SubmitRequest.OrderProperties.TimeInForce);
Assert.AreEqual(timeInForce ?? TimeInForce.GoodTilCanceled, msftTicket.SubmitRequest.OrderProperties.TimeInForce);
Assert.IsTrue(limitOrderCanceled);
}
[Test]
public void MarketOrdersAreSupportedForFuturesOnExtendedMarketHours()
{
var algo = GetAlgorithm(out _, 1, 0);
var mockOrderProcessor = new Mock();
var mockRequest = new Mock(null, null, null, null, null, null, null, null, null, null);
var mockTicket = new OrderTicket(algo.Transactions, mockRequest.Object);
mockOrderProcessor.Setup(m => m.Process(It.IsAny())).Returns(mockTicket);
mockOrderProcessor.Setup(m => m.GetOrderTicket(It.IsAny())).Returns(mockTicket);
algo.Transactions.SetOrderProcessor(mockOrderProcessor.Object);
var es20h20 = algo.AddFutureContract(
QuantConnect.Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 3, 20)),
Resolution.Minute,
extendedMarketHours: true);
var es20h20FOP = algo.AddFutureOptionContract(
Symbol.CreateOption(es20h20.Symbol, Market.CME, OptionStyle.American, OptionRight.Call, 2550m, new DateTime(2020, 3, 20)),
Resolution.Minute);
//Set price to $25
Update(es20h20, 25);
Update(es20h20FOP, 25);
algo.Portfolio.SetCash(150000);
var testOrders = (DateTime dateTime) =>
{
algo.SetDateTime(dateTime);
var ticket = algo.Buy(es20h20.Symbol, 1);
Assert.AreEqual(OrderStatus.New, ticket.Status, $"Future buy market order status should be new at {dateTime}, but was {ticket.Status}");
ticket = algo.Sell(es20h20.Symbol, 1);
Assert.AreEqual(OrderStatus.New, ticket.Status, $"Future sell market order status should be new at {dateTime}, but was {ticket.Status}");
ticket = algo.Buy(es20h20FOP.Symbol, 1);
Assert.AreEqual(OrderStatus.New, ticket.Status, $"Future option buy market order status should be new at {dateTime}, but was {ticket.Status}");
ticket = algo.Sell(es20h20FOP.Symbol, 1);
Assert.AreEqual(OrderStatus.New, ticket.Status, $"Future option sell market order status should be new at {dateTime}, but was {ticket.Status}");
};
// October 7 to 11 (monday to friday). Testing pre-market hours
for (var i = 7; i <= 11; i++)
{
testOrders(new DateTime(2013, 10, i, 5, 0, 0));
}
// October 6 to 10 (sunday to thrusday). Testing post-market hours
for (var i = 6; i <= 10; i++)
{
testOrders(new DateTime(2013, 10, i, 23, 0, 0));
}
}
[Test]
public void MarketOnOpenOrdersNotSupportedForFutures()
{
var algo = GetAlgorithm(out _, 1, 0);
var es20h20 = algo.AddFutureContract(
QuantConnect.Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 3, 20)),
Resolution.Minute);
var ticket = algo.MarketOnOpenOrder(es20h20.Symbol, 1);
Assert.That(ticket, Has.Property("Status").EqualTo(OrderStatus.Invalid));
}
[Test]
public void OptionOrdersAreNotAllowedDuringASplit()
{
var algo = GetAlgorithm(out _, 1, 0);
var aapl = algo.AddEquity("AAPL");
var applOptionContract = algo.AddOptionContract(
Symbol.CreateOption(aapl.Symbol, Market.USA, OptionStyle.American, OptionRight.Call, 40m, new DateTime(2014, 07, 19)));
var splitDate = new DateTime(2014, 06, 09);
aapl.SetMarketPrice(new IndicatorDataPoint(splitDate, 650m));
applOptionContract.SetMarketPrice(new IndicatorDataPoint(splitDate, 5m));
algo.SetCurrentSlice(new Slice(splitDate, new[] { new Split(aapl.Symbol, splitDate, 650m, 1 / 7, SplitType.SplitOccurred) }, splitDate));
var ticket = algo.MarketOrder(applOptionContract.Symbol, 1);
Assert.AreEqual(OrderStatus.Invalid, ticket.Status);
Assert.IsTrue(ticket.SubmitRequest.Response.IsError);
Assert.AreEqual(OrderResponseErrorCode.OptionOrderOnStockSplit, ticket.SubmitRequest.Response.ErrorCode);
Assert.IsTrue(ticket.SubmitRequest.Response.ErrorMessage.Contains(
"Options orders are not allowed when a split occurred for its underlying stock", StringComparison.InvariantCulture));
}
[TestCase(OrderType.MarketOnOpen)]
[TestCase(OrderType.MarketOnClose)]
public void GoodTilDateTimeInForceNotSupportedForMOOAndMOCOrders(OrderType orderType)
{
var algorithm = GetAlgorithm(out var msft, 1, 0);
Update(msft, 25);
var orderProperties = new OrderProperties() { TimeInForce = TimeInForce.GoodTilDate(algorithm.Time.AddDays(1)) };
OrderTicket ticket;
switch (orderType)
{
case OrderType.MarketOnOpen:
ticket = algorithm.MarketOnOpenOrder(msft.Symbol, 1, orderProperties: orderProperties);
break;
case OrderType.MarketOnClose:
ticket = algorithm.MarketOnCloseOrder(msft.Symbol, 1, orderProperties: orderProperties);
break;
default:
Assert.Fail("Unexpected order type");
return;
}
Assert.AreEqual(OrderStatus.New, ticket.Status);
Assert.AreEqual(TimeInForce.GoodTilCanceled, ticket.SubmitRequest.OrderProperties.TimeInForce);
}
[Test]
public void EuropeanOptionsCannotBeExercisedBeforeExpiry()
{
var algo = GetAlgorithm(out _, 1, 0);
var optionExpiry = new DateTime(2020, 3, 20);
var indexSymbol = Symbol.Create("SPX", SecurityType.Index, Market.USA);
var optionSymbol = Symbol.CreateOption(indexSymbol, Market.USA, OptionStyle.European, OptionRight.Call, 1, optionExpiry);
var europeanOptionContract = algo.AddOptionContract(optionSymbol, Resolution.Minute);
europeanOptionContract.SetMarketPrice(new TradeBar() { Symbol = europeanOptionContract.Symbol, Value = 1, Time = algo.Time });
europeanOptionContract.Holdings.SetHoldings(1, 1);
algo.SetDateTime(optionExpiry.AddDays(-1).AddHours(15));
var ticket = algo.ExerciseOption(europeanOptionContract.Symbol, 1);
Assert.AreEqual(OrderStatus.Invalid, ticket.Status);
Assert.AreEqual(OrderResponseErrorCode.EuropeanOptionNotExpiredOnExercise, ticket.SubmitRequest.Response.ErrorCode);
algo.SetDateTime(optionExpiry.AddHours(15));
ticket = algo.ExerciseOption(europeanOptionContract.Symbol, 1);
Assert.AreEqual(OrderStatus.New, ticket.Status);
}
[Test]
public void ComboOrderPreChecks()
{
var start = DateTime.UtcNow;
var algo = new AlgorithmStub();
algo.SetFinishedWarmingUp();
algo.AddEquity("SPY").SetMarketPrice(new TradeBar
{
Time = algo.Time,
Open = 10m,
High = 10,
Low = 10,
Close = 10,
Volume = 0,
Symbol = Symbols.SPY,
DataType = MarketDataType.TradeBar
});
algo.AddOptionContract(Symbols.SPY_C_192_Feb19_2016);
var legs = new List
{
new Leg { Symbol = Symbols.SPY, Quantity = 1 },
new Leg { Symbol = Symbols.SPY_C_192_Feb19_2016, Quantity = 1 },
};
// the underlying has a price but the option does not
var result = algo.ComboMarketOrder(legs, 1);
Assert.AreEqual(1, result.Count);
Assert.AreEqual(OrderStatus.Invalid, result.Single().Status);
Assert.IsTrue(result.Single().SubmitRequest.Response.IsError);
Assert.IsTrue(result.Single().SubmitRequest.Response.ErrorMessage.Contains("does not have an accurate price"));
Assert.IsTrue(DateTime.UtcNow - start < TimeSpan.FromMilliseconds(500));
}
[TestCase(new int[] { 1, 2 }, false)]
[TestCase(new int[] { -1, 10 }, false)]
[TestCase(new int[] { 2, -5 }, false)]
[TestCase(new int[] { 1, 2, 3 }, false)]
[TestCase(new int[] { 200, -11, 7 }, false)]
[TestCase(new int[] { 10, 20 }, true)]
[TestCase(new int[] { -10, 100 }, true)]
[TestCase(new int[] { 20, -50 }, true)]
[TestCase(new int[] { 10, 20, 30 }, true)]
[TestCase(new int[] { 1000, -55, 35 }, true)]
public void ComboOrderLegsRatiosAreValidated(int[] quantities, bool shouldThrow)
{
var algo = GetAlgorithm(out _, 1, 0);
var legs = quantities.Select(q => Leg.Create(Symbols.MSFT, q)).ToList();
if (shouldThrow)
{
Assert.Throws(() => algo.ComboMarketOrder(legs, 1));
Assert.Throws(() => algo.ComboLimitOrder(legs, 1, 100));
Assert.Throws(() => algo.ComboLegLimitOrder(legs.Select(leg =>
{
leg.OrderPrice = 10;
return leg;
}).ToList(), 1));
}
else
{
Assert.DoesNotThrow(() => algo.ComboMarketOrder(legs, 1));
Assert.DoesNotThrow(() => algo.ComboLimitOrder(legs, 1, 100));
Assert.DoesNotThrow(() => algo.ComboLegLimitOrder(legs.Select(leg =>
{
leg.OrderPrice = 10;
return leg;
}).ToList(), 1));
}
}
[Test]
public void MarketOnCloseOrdersSubmissionTimeCheck([Values] bool beforeLatestSubmissionTime)
{
var algo = GetAlgorithm(out _, 1, 0);
algo.SetTimeZone(TimeZones.London);
algo.SetDateTime(new DateTime(2023, 02, 16));
var es20h20 = algo.AddFutureContract(
Symbol.CreateFuture(Futures.Indices.SP500EMini, Market.CME, new DateTime(2020, 3, 20)),
Resolution.Minute);
es20h20.SetMarketPrice(new Tick(algo.Time, es20h20.Symbol, 1, 1));
var dateTimeInExchangeTimeZone = algo.Time.Date + new TimeSpan(17, 0, 0) - MarketOnCloseOrder.SubmissionTimeBuffer;
if (!beforeLatestSubmissionTime)
{
dateTimeInExchangeTimeZone += TimeSpan.FromSeconds(1);
}
algo.SetDateTime(dateTimeInExchangeTimeZone.ConvertTo(es20h20.Exchange.TimeZone, algo.TimeZone));
var ticket = algo.MarketOnCloseOrder(es20h20.Symbol, 1);
if (!beforeLatestSubmissionTime)
{
Assert.AreEqual(OrderStatus.Invalid, ticket.Status);
Assert.AreEqual(OrderResponseErrorCode.MarketOnCloseOrderTooLate, ticket.SubmitRequest.Response.ErrorCode);
}
else
{
Assert.AreNotEqual(OrderStatus.Invalid, ticket.Status, ticket.SubmitRequest.Response.ErrorMessage);
}
}
private QCAlgorithm GetAlgorithm(out Security msft, decimal leverage, decimal fee)
{
//Initialize algorithm
var algo = new QCAlgorithm();
algo.Settings.MinimumOrderMarginPortfolioPercentage = 0;
algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo));
algo.AddSecurity(SecurityType.Equity, "MSFT");
algo.SetCash(100000);
algo.SetFinishedWarmingUp();
algo.Securities[Symbols.MSFT].FeeModel = new ConstantFeeModel(fee);
algo.SetLiveMode(false);
_fakeOrderProcessor = new FakeOrderProcessor();
algo.Transactions.SetOrderProcessor(_fakeOrderProcessor);
msft = algo.Securities[Symbols.MSFT];
msft.SetLeverage(leverage);
algo.SetCurrentSlice(new Slice(DateTime.MinValue, Enumerable.Empty(), DateTime.MinValue));
return algo;
}
private QCAlgorithm GetAlgorithm(out Security msft, decimal initialMarginRequirement, decimal maintenanceMarginRequirement, decimal fee)
{
//Initialize algorithm
var algo = new QCAlgorithm();
algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo));
algo.AddSecurity(SecurityType.Equity, "MSFT");
algo.SetCash(100000);
algo.SetFinishedWarmingUp();
algo.Securities[Symbols.MSFT].FeeModel = new ConstantFeeModel(fee);
_fakeOrderProcessor = new FakeOrderProcessor();
algo.Transactions.SetOrderProcessor(_fakeOrderProcessor);
msft = algo.Securities[Symbols.MSFT];
msft.BuyingPowerModel = new SecurityMarginModel(initialMarginRequirement, maintenanceMarginRequirement, 0);
return algo;
}
private void Update(Security security, decimal close)
{
security.SetMarketPrice(new TradeBar
{
Time = DateTime.Now,
Symbol = security.Symbol,
Open = close,
High = close,
Low = close,
Close = close
});
}
private bool HasSufficientBuyingPowerForOrder(decimal orderQuantity, Security security, IAlgorithm algo)
{
var order = new MarketOrder(security.Symbol, orderQuantity, DateTime.UtcNow);
_fakeOrderProcessor.AddTicket(order.ToOrderTicket(algo.Transactions));
var hashSufficientBuyingPower = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(algo.Portfolio,
security, new MarketOrder(security.Symbol, orderQuantity, DateTime.UtcNow));
return hashSufficientBuyingPower.IsSufficient;
}
private static object[] LiquidateWorksAsExpectedTestCases =
{
new object[] { Language.CSharp, true, true, TimeInForce.Day },
new object[] { Language.CSharp, false, true, TimeInForce.Day },
new object[] { Language.CSharp, null, true, TimeInForce.Day },
new object[] { Language.Python, null, true, TimeInForce.Day },
new object[] { Language.CSharp, true, false, TimeInForce.Day },
new object[] { Language.CSharp, false, false, TimeInForce.Day },
new object[] { Language.CSharp, null, false, TimeInForce.Day },
new object[] { Language.Python, null, false, TimeInForce.Day },
new object[] { Language.CSharp, true, true, null },
new object[] { Language.CSharp, false, true, null },
new object[] { Language.CSharp, null, true, null },
new object[] { Language.Python, null, true, null },
new object[] { Language.CSharp, true, false, null },
new object[] { Language.CSharp, false, false, null },
new object[] { Language.CSharp, null, false, null },
new object[] { Language.Python, null, false, null }
};
private static object[] SetHoldingReturnsOrderTicketsTestCases =
{
new object[] { new List(), true, new Dictionary { { Symbols.AAPL, -3 }, { Symbols.IBM, -3 }, { Symbols.SPY, -3 } }, "(Empty, true)"},
new object[] { new List(), false, new Dictionary(), "(Empty, false)" },
new object[] { new List() { Symbols.IBM }, true, new Dictionary { { Symbols.AAPL, -3m }, { Symbols.IBM, 335m }, { Symbols.SPY, -3m } }, "(OneSymbol, true)" },
new object[] { new List() { Symbols.IBM }, false, new Dictionary { { Symbols.IBM, 335m } }, "(OneSymbol, true)" },
new object[] { new List() { Symbols.AAPL, Symbols.SPY }, true, new Dictionary { { Symbols.AAPL, 504m }, { Symbols.IBM, -3m }, { Symbols.SPY, 250m } }, "(MultipleSymbols, true)" },
new object[] { new List() { Symbols.AAPL, Symbols.SPY }, false, new Dictionary { { Symbols.AAPL, 504m }, { Symbols.SPY, 250m } }, "(MultipleSymbols, false)" },
};
}
}