900 lines
43 KiB
C#
900 lines
43 KiB
C#
/*
|
|
* 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 NUnit.Framework;
|
|
using QuantConnect.Data;
|
|
using QuantConnect.Util;
|
|
using QuantConnect.Orders;
|
|
using QuantConnect.Algorithm;
|
|
using QuantConnect.Brokerages;
|
|
using QuantConnect.Interfaces;
|
|
using QuantConnect.Securities;
|
|
using QuantConnect.Data.Market;
|
|
using QuantConnect.Orders.Fees;
|
|
using QuantConnect.Securities.Option;
|
|
using QuantConnect.Tests.Engine.DataFeeds;
|
|
using Option = QuantConnect.Securities.Option.Option;
|
|
|
|
namespace QuantConnect.Tests.Common.Securities
|
|
{
|
|
[TestFixture]
|
|
public class SecurityMarginModelTests
|
|
{
|
|
private static Symbol _symbol;
|
|
private static FakeOrderProcessor _fakeOrderProcessor;
|
|
|
|
[TestCase(1)]
|
|
[TestCase(2)]
|
|
[TestCase(50)]
|
|
public void MarginRemainingForLeverage(decimal leverage)
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.SetCash(1000);
|
|
|
|
var spy = InitAndGetSecurity(algorithm, 0);
|
|
spy.Holdings.SetHoldings(25, 100);
|
|
spy.SetLeverage(leverage);
|
|
|
|
var spyMarginAvailable = spy.Holdings.HoldingsValue - spy.Holdings.HoldingsValue * (1 / leverage);
|
|
|
|
var marginRemaining = algorithm.Portfolio.MarginRemaining;
|
|
Assert.AreEqual(1000 + spyMarginAvailable, marginRemaining);
|
|
}
|
|
|
|
[TestCase(1)]
|
|
[TestCase(2)]
|
|
[TestCase(50)]
|
|
public void MarginUsedForPositionWhenPriceDrops(decimal leverage)
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
|
|
// (1000 * 20) = 20k
|
|
// Initial and maintenance margin = (1000 * 20) / leverage = X
|
|
var spy = InitAndGetSecurity(algorithm, 0);
|
|
spy.Holdings.SetHoldings(20, 1000);
|
|
spy.SetLeverage(leverage);
|
|
|
|
// Drop 40% price from $20 to $12
|
|
// 1000 * 12 = 12k
|
|
Update(spy, 12);
|
|
|
|
var marginForPosition = spy.BuyingPowerModel.GetReservedBuyingPowerForPosition(
|
|
new ReservedBuyingPowerForPositionParameters(spy)).AbsoluteUsedBuyingPower;
|
|
Assert.AreEqual(1000 * 12 / leverage, marginForPosition);
|
|
}
|
|
|
|
[TestCase(1)]
|
|
[TestCase(2)]
|
|
[TestCase(50)]
|
|
public void MarginUsedForPositionWhenPriceIncreases(decimal leverage)
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.SetCash(1000);
|
|
|
|
// (1000 * 20) = 20k
|
|
// Initial and maintenance margin = (1000 * 20) / leverage = X
|
|
var spy = InitAndGetSecurity(algorithm, 0);
|
|
spy.Holdings.SetHoldings(25, 1000);
|
|
spy.SetLeverage(leverage);
|
|
|
|
// Increase from $20 to $40
|
|
// 1000 * 40 = 400000
|
|
Update(spy, 40);
|
|
|
|
var marginForPosition = spy.BuyingPowerModel.GetReservedBuyingPowerForPosition(
|
|
new ReservedBuyingPowerForPositionParameters(spy)).AbsoluteUsedBuyingPower;
|
|
Assert.AreEqual(1000 * 40 / leverage, marginForPosition);
|
|
}
|
|
|
|
[Test]
|
|
public void ZeroTargetWithZeroHoldingsIsNotAnError()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algorithm, 0);
|
|
|
|
var model = new SecurityMarginModel();
|
|
var result = model.GetMaximumOrderQuantityForTargetBuyingPower(algorithm.Portfolio, security, 0, 0);
|
|
|
|
Assert.AreEqual(0, result.Quantity);
|
|
Assert.IsTrue(result.Reason.IsNullOrEmpty());
|
|
Assert.IsFalse(result.IsError);
|
|
}
|
|
|
|
[TestCase(0)]
|
|
[TestCase(1)]
|
|
[TestCase(-1)]
|
|
public void ReturnsMinimumOrderValueReason(decimal holdings)
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algorithm, 0);
|
|
var model = new SecurityMarginModel();
|
|
security.Holdings.SetHoldings(security.Price, holdings);
|
|
var currentSignedUsedMargin = model.GetInitialMarginRequirement(security, security.Holdings.Quantity);
|
|
var totalPortfolioValue = algorithm.Portfolio.TotalPortfolioValue;
|
|
var sign = Math.Sign(security.Holdings.Quantity) == 0 ? 1 : Math.Sign(security.Holdings.Quantity);
|
|
// we increase it slightly, should not trigger a new order because it's increasing final margin usage, rounds down
|
|
var newTarget = currentSignedUsedMargin / (totalPortfolioValue) + 0.00001m * sign;
|
|
|
|
var result = model.GetMaximumOrderQuantityForTargetBuyingPower(algorithm.Portfolio, security, newTarget, 0);
|
|
Assert.AreEqual(0m, result.Quantity);
|
|
Assert.IsFalse(result.IsError);
|
|
Assert.IsTrue(result.Reason.Contains("The order quantity is less than the lot size of", StringComparison.InvariantCultureIgnoreCase));
|
|
}
|
|
|
|
[TestCase(1)]
|
|
[TestCase(-1)]
|
|
public void ReducesPositionWhenMarginAboveTargetWhenNegativeFreeMargin(decimal holdings)
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algorithm, 0);
|
|
var model = new SecurityMarginModel();
|
|
security.Holdings.SetHoldings(security.Price, holdings);
|
|
|
|
var security2 = InitAndGetSecurity(algorithm, 0, symbol: "AAPL");
|
|
// eat up all our TPV
|
|
security2.Holdings.SetHoldings(security.Price, (algorithm.Portfolio.TotalPortfolioValue / security.Price) * 2);
|
|
|
|
var currentSignedUsedMargin = model.GetInitialMarginRequirement(security, security.Holdings.Quantity);
|
|
var totalPortfolioValue = algorithm.Portfolio.TotalPortfolioValue;
|
|
var sign = Math.Sign(security.Holdings.Quantity) == 0 ? 1 : Math.Sign(security.Holdings.Quantity);
|
|
// we inverse the sign here so that new target is less than current, we expect a reduction
|
|
var newTarget = currentSignedUsedMargin / (totalPortfolioValue) + 0.00001m * sign * -1;
|
|
|
|
Assert.IsTrue(0 > algorithm.Portfolio.MarginRemaining);
|
|
var result = model.GetMaximumOrderQuantityForTargetBuyingPower(algorithm.Portfolio, security, newTarget, 0);
|
|
// Reproduces GH issue #5763 a small Reduction in the target should reduce the position
|
|
Assert.AreEqual(1m * sign * -1, result.Quantity);
|
|
Assert.IsFalse(result.IsError);
|
|
}
|
|
|
|
[TestCase(1, 0)]
|
|
[TestCase(-1, 0)]
|
|
[TestCase(1, 0.001d)]
|
|
[TestCase(-1, 0.001d)]
|
|
public void ReducesPositionWhenMarginAboveTargetBasedOnSetting(decimal holdings, decimal minimumOrderMarginPortfolioPercentage)
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algorithm, 0);
|
|
var model = new SecurityMarginModel();
|
|
security.Holdings.SetHoldings(security.Price, holdings);
|
|
|
|
var currentSignedUsedMargin = model.GetInitialMarginRequirement(security, security.Holdings.Quantity);
|
|
var totalPortfolioValue = algorithm.Portfolio.TotalPortfolioValue;
|
|
var sign = Math.Sign(security.Holdings.Quantity) == 0 ? 1 : Math.Sign(security.Holdings.Quantity);
|
|
// we inverse the sign here so that new target is less than current, we expect a reduction
|
|
var newTarget = currentSignedUsedMargin / (totalPortfolioValue) + 0.00001m * sign * -1;
|
|
|
|
var result = model.GetMaximumOrderQuantityForTargetBuyingPower(algorithm.Portfolio, security, newTarget, minimumOrderMarginPortfolioPercentage);
|
|
|
|
if (minimumOrderMarginPortfolioPercentage == 0)
|
|
{
|
|
// Reproduces GH issue #5763 a small Reduction in the target should reduce the position
|
|
Assert.AreEqual(1m * sign * -1, result.Quantity);
|
|
Assert.IsFalse(result.IsError);
|
|
}
|
|
else
|
|
{
|
|
Assert.AreEqual(0, result.Quantity);
|
|
Assert.IsFalse(result.IsError);
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void ZeroTargetWithNonZeroHoldingsReturnsNegativeOfQuantity()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algorithm, 0);
|
|
security.Holdings.SetHoldings(200, 10);
|
|
|
|
var model = new SecurityMarginModel();
|
|
var result = model.GetMaximumOrderQuantityForTargetBuyingPower(algorithm.Portfolio, security, 0, 0);
|
|
|
|
Assert.AreEqual(-10, result.Quantity);
|
|
Assert.IsTrue(result.Reason.IsNullOrEmpty());
|
|
Assert.IsFalse(result.IsError);
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullLong()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security));
|
|
// (100000 * 2 * 0.9975 setHoldingsBuffer) / 25 - fee ~=7979m
|
|
Assert.AreEqual(7979m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullLong_NonAccountCurrency_ZeroQuoteCurrency()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.Portfolio.CashBook.Clear();
|
|
algorithm.Portfolio.SetAccountCurrency("EUR");
|
|
algorithm.Portfolio.SetCash(10000);
|
|
// We don't have quote currency - we will get a "loan"
|
|
algorithm.Portfolio.SetCash(Currencies.USD, 0, 0.88m);
|
|
var security = InitAndGetSecurity(algorithm, 5);
|
|
|
|
algorithm.Settings.FreePortfolioValue =
|
|
algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage;
|
|
|
|
var actual = algorithm.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security));
|
|
// (10000 * 2 * 0.9975 setHoldingsBuffer) / 25 * 0.88 conversion rate - 5 USD fee * 0.88 conversion rate ~=906m
|
|
Assert.AreEqual(906m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algorithm));
|
|
}
|
|
|
|
[TestCase("Long")]
|
|
[TestCase("Short")]
|
|
public void GetReservedBuyingPowerForPosition_NonAccountCurrency_ZeroQuoteCurrency(string position)
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.Portfolio.CashBook.Clear();
|
|
algorithm.Portfolio.SetAccountCurrency("EUR");
|
|
algorithm.Portfolio.SetCash(10000);
|
|
algorithm.Portfolio.SetCash(Currencies.USD, 0, 0.88m);
|
|
var security = InitAndGetSecurity(algorithm, 5);
|
|
security.Holdings.SetHoldings(security.Price,
|
|
(position == "Long" ? 1 : -1) * 100);
|
|
|
|
var actual = security.BuyingPowerModel.GetReservedBuyingPowerForPosition(new ReservedBuyingPowerForPositionParameters(security));
|
|
// 100quantity * 25price * 0.88rate * 0.5 MaintenanceMarginRequirement = 1100
|
|
Assert.AreEqual(1100, actual.AbsoluteUsedBuyingPower);
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullLong_NonAccountCurrency()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.Portfolio.CashBook.Clear();
|
|
algorithm.Portfolio.SetAccountCurrency("EUR");
|
|
algorithm.Portfolio.SetCash(10000);
|
|
// We have 1000 USD too
|
|
algorithm.Portfolio.SetCash(Currencies.USD, 1000, 0.88m);
|
|
var security = InitAndGetSecurity(algorithm, 5);
|
|
|
|
algorithm.Settings.FreePortfolioValue =
|
|
algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage;
|
|
|
|
var actual = algorithm.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security));
|
|
// ((10000 + 1000 USD * 0.88 rate) * 2 * 0.9975 setHoldingsBuffer) / 25 * 0.88 rate - 5 USD fee * 0.88 rate ~=986m
|
|
Assert.AreEqual(986m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algorithm));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_Long_TooBigOfATarget()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security) + 0.1m);
|
|
// (100000 * 2.1* 0.9975 setHoldingsBuffer) / 25 - fee ~=8378m
|
|
Assert.AreEqual(8378m, actual);
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_Long_TooBigOfATarget_NonAccountCurrency()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.Portfolio.CashBook.Clear();
|
|
algorithm.Portfolio.SetAccountCurrency("EUR");
|
|
algorithm.Portfolio.SetCash(10000);
|
|
// We don't have quote currency - we will get a "loan"
|
|
algorithm.Portfolio.SetCash(Currencies.USD, 0, 0.88m);
|
|
var security = InitAndGetSecurity(algorithm, 5);
|
|
|
|
algorithm.Settings.FreePortfolioValue =
|
|
algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage;
|
|
|
|
var actual = algorithm.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security) + 0.1m);
|
|
// (10000 * 2.1 * 0.9975 setHoldingsBuffer) / 25 * 0.88 conversion rate - 5 USD fee * 0.88 conversion rate ~=951m
|
|
Assert.AreEqual(951m, actual);
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual, security, algorithm));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullShort()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, -1m * security.BuyingPowerModel.GetLeverage(security));
|
|
// (100000 * 2 * 0.9975 setHoldingsBuffer) / 25 - fee~=-7979m
|
|
Assert.AreEqual(-7979m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullShort_NonAccountCurrency_ZeroQuoteCurrency()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.Portfolio.CashBook.Clear();
|
|
algorithm.Portfolio.SetAccountCurrency("EUR");
|
|
algorithm.Portfolio.SetCash(10000);
|
|
algorithm.Portfolio.SetCash(Currencies.USD, 0, 0.88m);
|
|
var security = InitAndGetSecurity(algorithm, 5);
|
|
|
|
algorithm.Settings.FreePortfolioValue =
|
|
algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage;
|
|
|
|
var actual = algorithm.CalculateOrderQuantity(_symbol, -1m * security.BuyingPowerModel.GetLeverage(security));
|
|
// (10000 * - 2 * 0.9975 setHoldingsBuffer) / 25 * 0.88 conversion rate - 5 USD fee * 0.88 conversion rate ~=906m
|
|
Assert.AreEqual(-906m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algorithm));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullShort_NonAccountCurrency()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.Portfolio.CashBook.Clear();
|
|
algorithm.Portfolio.SetAccountCurrency("EUR");
|
|
algorithm.Portfolio.SetCash(10000);
|
|
algorithm.Portfolio.SetCash(Currencies.USD, 1000, 0.88m);
|
|
var security = InitAndGetSecurity(algorithm, 5);
|
|
|
|
algorithm.Settings.FreePortfolioValue =
|
|
algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage;
|
|
|
|
var actual = algorithm.CalculateOrderQuantity(_symbol, -1m * security.BuyingPowerModel.GetLeverage(security));
|
|
// ((10000 + 1000 * 0.88)* - 2 * 0.9975 setHoldingsBuffer) / 25 * 0.88 conversion rate - 5 USD fee * 0.88 conversion rate ~=986m
|
|
Assert.AreEqual(-986m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algorithm));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_Short_TooBigOfATarget()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, -1m * security.BuyingPowerModel.GetLeverage(security) - 0.1m);
|
|
// (100000 * - 2.1m * 0.9975 setHoldingsBuffer) / 25 - fee~=-8378m
|
|
Assert.AreEqual(-8378m, actual);
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_Short_TooBigOfATarget_NonAccountCurrency()
|
|
{
|
|
var algorithm = GetAlgorithm();
|
|
algorithm.Portfolio.CashBook.Clear();
|
|
algorithm.Portfolio.SetAccountCurrency("EUR");
|
|
algorithm.Portfolio.SetCash(10000);
|
|
algorithm.Portfolio.SetCash(Currencies.USD, 0, 0.88m);
|
|
var security = InitAndGetSecurity(algorithm, 5);
|
|
|
|
algorithm.Settings.FreePortfolioValue =
|
|
algorithm.Portfolio.TotalPortfolioValue * algorithm.Settings.FreePortfolioValuePercentage;
|
|
|
|
var actual = algorithm.CalculateOrderQuantity(_symbol, -1m * security.BuyingPowerModel.GetLeverage(security) - 0.1m);
|
|
// (10000 * - 2.1 * 0.9975 setHoldingsBuffer) / 25 * 0.88 conversion rate - 5 USD fee * 0.88 conversion rate ~=951m
|
|
Assert.AreEqual(-951m, actual);
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual, security, algorithm));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullLong_NoFee()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 0);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security));
|
|
// (100000 * 2 * 0.9975 setHoldingsBuffer) / 25 =7980m
|
|
Assert.AreEqual(7980m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_Long_TooBigOfATarget_NoFee()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 0);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security) + 0.1m);
|
|
// (100000 * 2.1m* 0.9975 setHoldingsBuffer) / 25 = 8379m
|
|
Assert.AreEqual(8379m, actual);
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_ZeroToFullShort_NoFee()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 0);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, -1m * security.BuyingPowerModel.GetLeverage(security));
|
|
var order = new MarketOrder(_symbol, actual, DateTime.UtcNow);
|
|
// (100000 * 2 * 0.9975 setHoldingsBuffer) / 25 = -7980m
|
|
Assert.AreEqual(-7980m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void SetHoldings_Short_TooBigOfATarget_NoFee()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 0);
|
|
var actual = algo.CalculateOrderQuantity(_symbol, -1m * security.BuyingPowerModel.GetLeverage(security) - 0.1m);
|
|
// (100000 * -2.1 * 0.9975 setHoldingsBuffer) / 25 = -8379m
|
|
Assert.AreEqual(-8379m, actual);
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentDefault_Equity()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Equity);
|
|
var model = security.BuyingPowerModel;
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// (100000 * 2 * 0.9975) / 25 - 1 order due to fees
|
|
Assert.AreEqual(7979m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
Assert.AreEqual(algo.Portfolio.Cash, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentAppliesForCashAccount_Equity()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Equity);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(1, requiredFreeBuyingPowerPercent);
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// (100000 * 1 * 0.95 * 0.9975) / 25 - 1 order due to fees
|
|
Assert.AreEqual(3790m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual * 1.0025m, security, algo));
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual * 1.0025m + security.SymbolProperties.LotSize + 9, security, algo));
|
|
var expectedBuyingPower = algo.Portfolio.Cash * (1 - requiredFreeBuyingPowerPercent);
|
|
Assert.AreEqual(expectedBuyingPower, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentAppliesForMarginAccount_Equity()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Equity);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(2, requiredFreeBuyingPowerPercent);
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// (100000 * 2 * 0.95 * 0.9975) / 25 - 1 order due to fees
|
|
Assert.AreEqual(7580m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual * 1.0025m, security, algo));
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual * 1.0025m + security.SymbolProperties.LotSize + 9, security, algo));
|
|
var expectedBuyingPower = algo.Portfolio.Cash * (1 - requiredFreeBuyingPowerPercent);
|
|
Assert.AreEqual(expectedBuyingPower, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentCashAccountWithLongHoldings_Equity()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Equity);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(1, requiredFreeBuyingPowerPercent);
|
|
security.Holdings.SetHoldings(25, 2000);
|
|
security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(algo.Portfolio, security, DateTime.UtcNow.AddDays(-10), new CashAmount(-2000 * 25, Currencies.USD), null));
|
|
|
|
// Margin remaining 50k + used 50k + initial margin 50k - 5k free buying power percent (5% of 100k)
|
|
Assert.AreEqual(145000, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Sell));
|
|
// Margin remaining 50k - 5k free buying power percent (5% of 100k)
|
|
Assert.AreEqual(45000, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, -1m * model.GetLeverage(security));
|
|
// ((100k - 5) * -1 * 0.95 * 0.9975 - (50k holdings)) / 25 - 1 order due to fees
|
|
Assert.AreEqual(-5790m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual * 1.0025m, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentMarginAccountWithLongHoldings_Equity()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Equity);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(2, requiredFreeBuyingPowerPercent);
|
|
security.Holdings.SetHoldings(25, 2000);
|
|
security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(algo.Portfolio, security, DateTime.UtcNow.AddDays(-10), new CashAmount(-2000 * 25, Currencies.USD), null));
|
|
|
|
// Margin remaining 75k + used 25k + initial margin 25k - 5k free buying power percent (5% of 100k)
|
|
Assert.AreEqual(120000, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Sell));
|
|
// Margin remaining 75k - 5k free buying power percent
|
|
Assert.AreEqual(70000, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, -1m * model.GetLeverage(security));
|
|
// ((100k - 5) * -2 * 0.95 * 0.9975 - (50k holdings)) / 25 - 1 order due to fees
|
|
Assert.AreEqual(-9580m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual * 1.0025m, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentMarginAccountWithShortHoldings_Equity()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Equity);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(2, requiredFreeBuyingPowerPercent);
|
|
security.Holdings.SetHoldings(25, -2000);
|
|
security.SettlementModel.ApplyFunds(new ApplyFundsSettlementModelParameters(algo.Portfolio, security, DateTime.UtcNow.AddDays(-10), new CashAmount(2000 * 25, Currencies.USD), null));
|
|
|
|
// Margin remaining 75k + used 25k + initial margin 25k - 5k free buying power percent (5% of 100k)
|
|
Assert.AreEqual(120000, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
// Margin remaining 75k - 5k free buying power percent
|
|
Assert.AreEqual(70000, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Sell));
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// ((100k - 5) * 2 * 0.95 * 0.9975 - (-50k holdings)) / 25 - 1 order due to fees
|
|
Assert.AreEqual(9580m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
Assert.IsFalse(HasSufficientBuyingPowerForOrder(actual * 1.0025m, security, algo));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentDefault_Option()
|
|
{
|
|
const decimal price = 25m;
|
|
const decimal underlyingPrice = 25m;
|
|
|
|
var tz = TimeZones.NewYork;
|
|
var equity = new QuantConnect.Securities.Equity.Equity(
|
|
SecurityExchangeHours.AlwaysOpen(tz),
|
|
new SubscriptionDataConfig(typeof(TradeBar), Symbols.SPY, Resolution.Minute, tz, tz, true, false, false),
|
|
new Cash(Currencies.USD, 0, 1m),
|
|
SymbolProperties.GetDefault(Currencies.USD),
|
|
ErrorCurrencyConverter.Instance,
|
|
RegisteredSecurityDataTypesProvider.Null
|
|
);
|
|
equity.SetMarketPrice(new Tick { Value = underlyingPrice });
|
|
|
|
var optionPutSymbol = Symbol.CreateOption(Symbols.SPY, Market.USA, OptionStyle.American, OptionRight.Put, 207m, new DateTime(2015, 02, 27));
|
|
var security = new Option(
|
|
SecurityExchangeHours.AlwaysOpen(tz),
|
|
new SubscriptionDataConfig(typeof(TradeBar), optionPutSymbol, Resolution.Minute, tz, tz, true, false, false),
|
|
new Cash(Currencies.USD, 0, 1m),
|
|
new OptionSymbolProperties("", Currencies.USD, 100, 0.01m, 1),
|
|
ErrorCurrencyConverter.Instance,
|
|
RegisteredSecurityDataTypesProvider.Null
|
|
);
|
|
security.SetMarketPrice(new Tick { Value = price });
|
|
security.Underlying = equity;
|
|
|
|
var algo = GetAlgorithm();
|
|
security.SetLocalTimeKeeper(algo.TimeKeeper.GetLocalTimeKeeper(tz));
|
|
var actual = security.BuyingPowerModel.GetMaximumOrderQuantityForTargetBuyingPower(
|
|
new GetMaximumOrderQuantityForTargetBuyingPowerParameters(algo.Portfolio, security, 1, 0)).Quantity;
|
|
|
|
// (100000 * 1) / (25 * 100 contract multiplier) - 1 order due to fees
|
|
Assert.AreEqual(39m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
Assert.AreEqual(algo.Portfolio.Cash, security.BuyingPowerModel.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentAppliesForCashAccount_Option()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Option);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(1, requiredFreeBuyingPowerPercent);
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// (100000 * 1 * 0.95) / (25 * 100 contract multiplier) - 1 order due to fees
|
|
Assert.AreEqual(37m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
var expectedBuyingPower = algo.Portfolio.Cash * (1 - requiredFreeBuyingPowerPercent);
|
|
Assert.AreEqual(expectedBuyingPower, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentAppliesForMarginAccount_Option()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Option);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(2, requiredFreeBuyingPowerPercent);
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// (100000 * 2 * 0.95) / (25 * 100 contract multiplier) - 1 order due to fees
|
|
Assert.AreEqual(75m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
var expectedBuyingPower = algo.Portfolio.Cash * (1 - requiredFreeBuyingPowerPercent);
|
|
Assert.AreEqual(expectedBuyingPower, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentDefault_Future()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Future, "ES", time: new DateTime(2020, 1, 27));
|
|
var model = security.BuyingPowerModel;
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// (100000 * 1 * 0.9975 ) / 6600 - 1 order due to fees
|
|
Assert.AreEqual(13m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
Assert.AreEqual(algo.Portfolio.Cash, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentAppliesForCashAccount_Future()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Cash);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Future);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(1, requiredFreeBuyingPowerPercent);
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// ((100000 - 5) * 1 * 0.95 * 0.9975 / (25 * 50)
|
|
Assert.AreEqual(75m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
var expectedBuyingPower = algo.Portfolio.Cash * (1 - requiredFreeBuyingPowerPercent);
|
|
Assert.AreEqual(expectedBuyingPower, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[Test]
|
|
public void FreeBuyingPowerPercentAppliesForMarginAccount_Future()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin);
|
|
var security = InitAndGetSecurity(algo, 5, SecurityType.Future);
|
|
var requiredFreeBuyingPowerPercent = 0.05m;
|
|
var model = security.BuyingPowerModel = new SecurityMarginModel(2, requiredFreeBuyingPowerPercent);
|
|
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * model.GetLeverage(security));
|
|
// ((100000 - 5) * 2 * 0.95 * 0.9975 / (25 * 50)
|
|
Assert.AreEqual(151m, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
var expectedBuyingPower = algo.Portfolio.Cash * (1 - requiredFreeBuyingPowerPercent);
|
|
Assert.AreEqual(expectedBuyingPower, model.GetBuyingPower(algo.Portfolio, security, OrderDirection.Buy));
|
|
}
|
|
|
|
[TestCase(0)]
|
|
[TestCase(10000)]
|
|
public void NonAccountCurrency_GetBuyingPower(decimal nonAccountCurrencyCash)
|
|
{
|
|
var algo = GetAlgorithm();
|
|
algo.Portfolio.CashBook.Clear();
|
|
algo.Portfolio.SetAccountCurrency("EUR");
|
|
algo.Portfolio.SetCash(10000);
|
|
algo.Portfolio.SetCash(Currencies.USD, nonAccountCurrencyCash, 0.88m);
|
|
var security = InitAndGetSecurity(algo, 0);
|
|
Assert.AreEqual(10000m + algo.Portfolio.CashBook[Currencies.USD].ValueInAccountCurrency,
|
|
algo.Portfolio.TotalPortfolioValue);
|
|
|
|
var quantity = security.BuyingPowerModel.GetBuyingPower(
|
|
new BuyingPowerParameters(algo.Portfolio, security, OrderDirection.Buy)).Value;
|
|
|
|
Assert.AreEqual(10000m + algo.Portfolio.CashBook[Currencies.USD].ValueInAccountCurrency,
|
|
quantity);
|
|
}
|
|
|
|
[Test]
|
|
public void NonAccountCurrencyFees()
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 0);
|
|
algo.SetCash("EUR", 0, 100);
|
|
security.FeeModel = new NonAccountCurrencyCustomFeeModel();
|
|
|
|
// ((100000 - 100 * 100) * 2 * 0.9975 / (25)
|
|
var actual = algo.CalculateOrderQuantity(_symbol, 1m * security.BuyingPowerModel.GetLeverage(security));
|
|
Assert.AreEqual(7182m, actual);
|
|
// ((100000 - 100 * 100) * 2 / (25)
|
|
var quantity = security.BuyingPowerModel.GetMaximumOrderQuantityForTargetBuyingPower(
|
|
algo.Portfolio, security, 1m, 0).Quantity;
|
|
Assert.AreEqual(7200m, quantity);
|
|
|
|
// the maximum order quantity can be executed
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(quantity, security, algo)); ;
|
|
}
|
|
|
|
[TestCase(1)]
|
|
[TestCase(-1)]
|
|
public void GetMaximumOrderQuantityForTargetDeltaBuyingPower_NoHoldings(int side)
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5);
|
|
|
|
// we use our entire buying power
|
|
var buyingPower = algo.Portfolio.MarginRemaining * side;
|
|
var actual = security.BuyingPowerModel.GetMaximumOrderQuantityForDeltaBuyingPower(
|
|
new GetMaximumOrderQuantityForDeltaBuyingPowerParameters(algo.Portfolio,
|
|
security,
|
|
buyingPower,
|
|
0)).Quantity;
|
|
|
|
// (100000 * 2 ) / 25 =8k - 1 fees
|
|
Assert.AreEqual(7999 * side, actual);
|
|
}
|
|
|
|
[TestCase(100, 510, false)]
|
|
[TestCase(-100, 510, false)]
|
|
[TestCase(-100, 50000, true)]
|
|
[TestCase(100, -510, false)]
|
|
[TestCase(-100, -510, false)]
|
|
[TestCase(100, -50000, true)]
|
|
public void GetMaximumOrderQuantityForTargetDeltaBuyingPower_WithHoldings(decimal quantity, decimal buyingPowerDelta, bool invertsSide)
|
|
{
|
|
// TPV = 100k
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 0);
|
|
|
|
// SPY @ $25 * Quantity Shares = Holdings
|
|
// Quantity = 100 -> 2500; TPV = 102500
|
|
// Quantity = -100 -> -2500; TPV = 97500
|
|
security.Holdings.SetHoldings(security.Price, quantity);
|
|
|
|
// Used Buying Power = Holdings / Leverage
|
|
// Target BP = Used BP + buyingPowerDelta
|
|
// Target Holdings = Target BP / Unit
|
|
// Max Order For Delta BP = Target Holdings - Current Holdings
|
|
|
|
// EX. -100 Quantity, 510 BP Delta.
|
|
// Used BP = -2500 / 2 = -1250
|
|
// Target BP = -1250 + 510 = -740
|
|
// Target Holdings = -740 / 12.5 = -59.2 -> ~-59
|
|
// Max Order = -59 - (-100) = 41
|
|
var actual = security.BuyingPowerModel.GetMaximumOrderQuantityForDeltaBuyingPower(
|
|
new GetMaximumOrderQuantityForDeltaBuyingPowerParameters(algo.Portfolio,
|
|
security,
|
|
buyingPowerDelta,
|
|
0)).Quantity;
|
|
|
|
// Calculate expected using logic above
|
|
var targetBuyingPower = ((quantity * (security.Price / security.Leverage)) + buyingPowerDelta);
|
|
var targetHoldings = (targetBuyingPower / (security.Price / security.Leverage));
|
|
targetHoldings -= (targetHoldings % security.SymbolProperties.LotSize);
|
|
|
|
var expectedQuantity = targetHoldings - quantity;
|
|
|
|
|
|
Assert.AreEqual(expectedQuantity, actual);
|
|
Assert.IsTrue(HasSufficientBuyingPowerForOrder(actual, security, algo));
|
|
|
|
if (invertsSide)
|
|
{
|
|
Assert.AreNotEqual(Math.Sign(quantity), Math.Sign(actual));
|
|
}
|
|
}
|
|
|
|
[TestCase(true, 1, 1)]
|
|
[TestCase(true, 1, -1)]
|
|
[TestCase(true, -1, -1)]
|
|
[TestCase(true, -1, 1)]
|
|
// reducing the position to target 0 is valid
|
|
[TestCase(false, 0, -1)]
|
|
[TestCase(false, 0, 1)]
|
|
public void NegativeMarginRemaining(bool isError, int target, int side)
|
|
{
|
|
var algo = GetAlgorithm();
|
|
var security = InitAndGetSecurity(algo, 5);
|
|
security.Holdings.SetHoldings(security.Price, 1000 * side);
|
|
algo.Portfolio.CashBook.Add(algo.AccountCurrency, -100000, 1);
|
|
var fakeOrderProcessor = new FakeOrderProcessor();
|
|
algo.Transactions.SetOrderProcessor(fakeOrderProcessor);
|
|
|
|
Assert.IsTrue(algo.Portfolio.MarginRemaining < 0);
|
|
|
|
var quantity = security.BuyingPowerModel.GetMaximumOrderQuantityForTargetBuyingPower(
|
|
new GetMaximumOrderQuantityForTargetBuyingPowerParameters(algo.Portfolio,
|
|
security,
|
|
target * side,
|
|
0)).Quantity;
|
|
if (!isError)
|
|
{
|
|
Assert.AreEqual(1000 * side * -1, quantity);
|
|
}
|
|
else
|
|
{
|
|
// even if we don't have margin 'GetMaximumOrderQuantityForTargetBuyingPower' doesn't care
|
|
Assert.AreNotEqual(0, quantity);
|
|
}
|
|
|
|
var order = new MarketOrder(security.Symbol, quantity, new DateTime(2020, 1, 1));
|
|
fakeOrderProcessor.AddTicket(order.ToOrderTicket(algo.Transactions));
|
|
var actual = security.BuyingPowerModel.HasSufficientBuyingPowerForOrder(
|
|
new HasSufficientBuyingPowerForOrderParameters(algo.Portfolio,
|
|
security,
|
|
order));
|
|
Assert.AreEqual(!isError, actual.IsSufficient);
|
|
}
|
|
|
|
private static QCAlgorithm GetAlgorithm()
|
|
{
|
|
SymbolCache.Clear();
|
|
// Initialize algorithm
|
|
var algo = new QCAlgorithm();
|
|
algo.SetFinishedWarmingUp();
|
|
_fakeOrderProcessor = new FakeOrderProcessor();
|
|
algo.Transactions.SetOrderProcessor(_fakeOrderProcessor);
|
|
return algo;
|
|
}
|
|
|
|
private static Security InitAndGetSecurity(QCAlgorithm algo, decimal fee, SecurityType securityType = SecurityType.Equity, string symbol = "SPY", DateTime? time = null)
|
|
{
|
|
algo.SubscriptionManager.SetDataManager(new DataManagerStub(algo));
|
|
Security security;
|
|
if (securityType == SecurityType.Equity)
|
|
{
|
|
security = algo.AddEquity(symbol);
|
|
_symbol = security.Symbol;
|
|
}
|
|
else if (securityType == SecurityType.Option)
|
|
{
|
|
security = algo.AddOption(symbol);
|
|
_symbol = security.Symbol;
|
|
}
|
|
else if (securityType == SecurityType.Future)
|
|
{
|
|
security = algo.AddFuture(symbol == "SPY" ? "ES" : symbol);
|
|
_symbol = security.Symbol;
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("SecurityType not implemented");
|
|
}
|
|
|
|
security.FeeModel = new ConstantFeeModel(fee);
|
|
Update(security, 25, time);
|
|
return security;
|
|
}
|
|
|
|
private static void Update(Security security, decimal close, DateTime? time = null)
|
|
{
|
|
security.SetMarketPrice(new TradeBar
|
|
{
|
|
Time = 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;
|
|
}
|
|
|
|
internal class NonAccountCurrencyCustomFeeModel : FeeModel
|
|
{
|
|
public string FeeCurrency = "EUR";
|
|
public decimal FeeAmount = 100m;
|
|
|
|
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
|
|
{
|
|
return new OrderFee(new CashAmount(FeeAmount, FeeCurrency));
|
|
}
|
|
}
|
|
}
|
|
}
|