250 lines
20 KiB
C#
250 lines
20 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 System.Collections.Generic;
|
|
using System.Linq;
|
|
using NUnit.Framework;
|
|
using QuantConnect.Data;
|
|
using QuantConnect.Data.Market;
|
|
using QuantConnect.Orders;
|
|
using QuantConnect.Securities;
|
|
using QuantConnect.Securities.Cfd;
|
|
using QuantConnect.Securities.Equity;
|
|
using QuantConnect.Securities.Forex;
|
|
using QuantConnect.Securities.Option;
|
|
|
|
namespace QuantConnect.Tests.Common.Orders
|
|
{
|
|
[TestFixture]
|
|
public class OrderTests
|
|
{
|
|
[Test, TestCaseSource(nameof(GetValueTestParameters))]
|
|
public void GetValueTest(ValueTestParameters parameters)
|
|
{
|
|
// By default the price for option exercise orders is 0, so we need to set it to the strike price
|
|
if (parameters.Order.Type == OrderType.OptionExercise)
|
|
{
|
|
parameters.Order.Price = parameters.Order.Symbol.ID.StrikePrice;
|
|
}
|
|
|
|
var value = parameters.Order.GetValue(parameters.Security);
|
|
Assert.AreEqual(parameters.ExpectedValue, value);
|
|
}
|
|
|
|
[TestCase(OrderDirection.Sell, 300, 0.1, true, 270)]
|
|
[TestCase(OrderDirection.Sell, 300, 30, false, 270)]
|
|
[TestCase(OrderDirection.Buy, 300, 0.1, true, 330)]
|
|
[TestCase(OrderDirection.Buy, 300, 30, false, 330)]
|
|
public void TrailingStopOrder_CalculatesStopPrice(OrderDirection direction, decimal marketPrice, decimal trailingAmount,
|
|
bool trailingAsPercentage, decimal expectedStopPrice)
|
|
{
|
|
var stopPrice = TrailingStopOrder.CalculateStopPrice(marketPrice, trailingAmount, trailingAsPercentage, direction);
|
|
Assert.AreEqual(expectedStopPrice, stopPrice);
|
|
}
|
|
|
|
[TestCase(OrderDirection.Sell, 269, 300, 0.1, true, 270)]
|
|
[TestCase(OrderDirection.Sell, 270, 300, 0.1, true, null)]
|
|
[TestCase(OrderDirection.Sell, 269, 300, 30, false, 270)]
|
|
[TestCase(OrderDirection.Sell, 270, 300, 30, false, null)]
|
|
[TestCase(OrderDirection.Buy, 331, 300, 0.1, true, 330)]
|
|
[TestCase(OrderDirection.Buy, 330, 300, 0.1, true, null)]
|
|
[TestCase(OrderDirection.Buy, 331, 300, 30, false, 330)]
|
|
[TestCase(OrderDirection.Buy, 330, 300, 30, false, null)]
|
|
public void TrailingStopOrder_UpdatesStopPriceIfNecessary(OrderDirection direction, decimal currentStopPrice, decimal marketPrice,
|
|
decimal trailingAmount, bool trailingAsPercentage, decimal? expectedStopPrice)
|
|
{
|
|
var updated = TrailingStopOrder.TryUpdateStopPrice(marketPrice, currentStopPrice, trailingAmount, trailingAsPercentage, direction,
|
|
out var updatedStopPrice);
|
|
|
|
if (expectedStopPrice.HasValue)
|
|
{
|
|
Assert.IsTrue(updated);
|
|
Assert.AreEqual(expectedStopPrice.Value, updatedStopPrice);
|
|
}
|
|
else
|
|
{
|
|
Assert.IsFalse(updated);
|
|
}
|
|
}
|
|
|
|
private static TestCaseData[] GetValueTestParameters()
|
|
{
|
|
const decimal delta = 1m;
|
|
const decimal price = 1.2345m;
|
|
const int quantity = 100;
|
|
const decimal pricePlusDelta = price + delta;
|
|
const decimal priceMinusDelta = price - delta;
|
|
var tz = TimeZones.NewYork;
|
|
|
|
var time = new DateTime(2016, 2, 4, 16, 0, 0).ConvertToUtc(tz);
|
|
|
|
var equity = new 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 = price});
|
|
|
|
var gbpCash = new Cash("GBP", 0, 1.46m);
|
|
var properties = SymbolProperties.GetDefault(gbpCash.Symbol);
|
|
var forex = new Forex(
|
|
SecurityExchangeHours.AlwaysOpen(tz),
|
|
gbpCash,
|
|
new Cash("EUR", 0, 0),
|
|
new SubscriptionDataConfig(typeof(TradeBar), Symbols.EURGBP, Resolution.Minute, tz, tz, true, false, false),
|
|
properties,
|
|
ErrorCurrencyConverter.Instance,
|
|
RegisteredSecurityDataTypesProvider.Null
|
|
);
|
|
forex.SetMarketPrice(new Tick {Value= price});
|
|
|
|
var eurCash = new Cash("EUR", 0, 1.12m);
|
|
properties = new SymbolProperties("Euro-Bund", eurCash.Symbol, 10, 0.1m, 1, string.Empty);
|
|
var cfd = new Cfd(
|
|
SecurityExchangeHours.AlwaysOpen(tz),
|
|
eurCash,
|
|
new SubscriptionDataConfig(typeof(TradeBar), Symbols.DE10YBEUR, Resolution.Minute, tz, tz, true, false, false),
|
|
properties,
|
|
ErrorCurrencyConverter.Instance,
|
|
RegisteredSecurityDataTypesProvider.Null
|
|
);
|
|
cfd.SetMarketPrice(new Tick { Value = price });
|
|
var multiplierTimesConversionRate = properties.ContractMultiplier*eurCash.ConversionRate;
|
|
|
|
var option = new Option(
|
|
SecurityExchangeHours.AlwaysOpen(tz),
|
|
new SubscriptionDataConfig(
|
|
typeof(TradeBar),
|
|
Symbols.SPY_P_192_Feb19_2016,
|
|
Resolution.Minute,
|
|
tz,
|
|
tz,
|
|
true,
|
|
false,
|
|
false
|
|
),
|
|
new Cash(Currencies.USD, 0, 1m),
|
|
new OptionSymbolProperties(SymbolProperties.GetDefault(Currencies.USD)),
|
|
ErrorCurrencyConverter.Instance,
|
|
RegisteredSecurityDataTypesProvider.Null
|
|
);
|
|
option.SetMarketPrice(new Tick { Value = price });
|
|
|
|
return new List<ValueTestParameters>
|
|
{
|
|
// equity orders
|
|
new ValueTestParameters("EquityLongMarketOrder", equity, new MarketOrder(Symbols.SPY, quantity, time), quantity*price),
|
|
new ValueTestParameters("EquityShortMarketOrder", equity, new MarketOrder(Symbols.SPY, -quantity, time), -quantity*price),
|
|
new ValueTestParameters("EquityLongLimitOrder", equity, new LimitOrder(Symbols.SPY, quantity, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("EquityShortLimit Order", equity, new LimitOrder(Symbols.SPY, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("EquityLongStopLimitOrder", equity, new StopLimitOrder(Symbols.SPY, quantity,.5m*priceMinusDelta, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("EquityShortStopLimitOrder", equity, new StopLimitOrder(Symbols.SPY, -quantity, 1.5m*pricePlusDelta, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("EquityLongStopMarketOrder", equity, new StopMarketOrder(Symbols.SPY, quantity, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("EquityLongStopMarketOrder", equity, new StopMarketOrder(Symbols.SPY, quantity, pricePlusDelta, time), quantity*price),
|
|
new ValueTestParameters("EquityShortStopMarketOrder", equity, new StopMarketOrder(Symbols.SPY, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("EquityShortStopMarketOrder", equity, new StopMarketOrder(Symbols.SPY, -quantity, priceMinusDelta, time), -quantity*price),
|
|
new ValueTestParameters("EquityLongTrailingStopOrderPriceMinusDelta", equity, new TrailingStopOrder(Symbols.SPY, quantity, priceMinusDelta, 0.1m, true, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("EquityLongTrailingStopOrderPricePlusDelta", equity, new TrailingStopOrder(Symbols.SPY, quantity, pricePlusDelta, 0.1m, true, time), quantity*price),
|
|
new ValueTestParameters("EquityShortTrailingStopOrderPricePlusDelta", equity, new TrailingStopOrder(Symbols.SPY, -quantity, pricePlusDelta, 0.1m, true, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("EquityShortTrailingStopOrderPriceMinusDelta", equity, new TrailingStopOrder(Symbols.SPY, -quantity, priceMinusDelta, 0.1m, true, time), -quantity*price),
|
|
new ValueTestParameters("EquityLongLimitIfTouchedOrder", equity, new LimitIfTouchedOrder(Symbols.SPY, quantity, 1.5m*pricePlusDelta, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("EquityShortLimitIfTouchedOrder", equity, new LimitIfTouchedOrder(Symbols.SPY, -quantity, .5m*priceMinusDelta, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
|
|
// forex orders
|
|
new ValueTestParameters("ForexLongMarketOrder", forex, new MarketOrder(Symbols.EURGBP, quantity, time), quantity*price*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortMarketOrder", forex, new MarketOrder(Symbols.EURGBP, -quantity, time), -quantity*price*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexLongLimitOrder", forex, new LimitOrder(Symbols.EURGBP, quantity, priceMinusDelta, time), quantity*priceMinusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortLimit Order", forex, new LimitOrder(Symbols.EURGBP, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexLongStopLimitOrder", forex, new StopLimitOrder(Symbols.EURGBP, quantity,.5m*priceMinusDelta, priceMinusDelta, time), quantity*priceMinusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortStopLimitOrder", forex, new StopLimitOrder(Symbols.EURGBP, -quantity, 1.5m*pricePlusDelta, pricePlusDelta, time), -quantity*pricePlusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexLongStopMarketOrder", forex, new StopMarketOrder(Symbols.EURGBP, quantity, priceMinusDelta, time), quantity*priceMinusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexLongStopMarketOrder", forex, new StopMarketOrder(Symbols.EURGBP, quantity, pricePlusDelta, time), quantity*price*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortStopMarketOrder", forex, new StopMarketOrder(Symbols.EURGBP, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortStopMarketOrder", forex, new StopMarketOrder(Symbols.EURGBP, -quantity, priceMinusDelta, time), -quantity*price*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexLongTrailingStopOrderPriceMinusDelta", forex, new TrailingStopOrder(Symbols.EURGBP, quantity, priceMinusDelta, 0.1m, true, time), quantity*priceMinusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexLongTrailingStopOrderPricePlusDelta", forex, new TrailingStopOrder(Symbols.EURGBP, quantity, pricePlusDelta, 0.1m, true, time), quantity*price*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortTrailingStopOrderPricePlusDelta", forex, new TrailingStopOrder(Symbols.EURGBP, -quantity, pricePlusDelta, 0.1m, true, time), -quantity*pricePlusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortTrailingStopOrderPriceMinusDelta", forex, new TrailingStopOrder(Symbols.EURGBP, -quantity, priceMinusDelta, 0.1m, true, time), -quantity*price*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexLongLimitIfTouchedOrder", forex, new LimitIfTouchedOrder(Symbols.EURGBP, quantity,1.5m*priceMinusDelta, priceMinusDelta, time), quantity*priceMinusDelta*forex.QuoteCurrency.ConversionRate),
|
|
new ValueTestParameters("ForexShortLimitIfTouchedOrder", forex, new LimitIfTouchedOrder(Symbols.EURGBP, -quantity, .5m*pricePlusDelta, pricePlusDelta, time), -quantity*pricePlusDelta*forex.QuoteCurrency.ConversionRate),
|
|
|
|
// cfd orders
|
|
new ValueTestParameters("CfdLongMarketOrder", cfd, new MarketOrder(Symbols.DE10YBEUR, quantity, time), quantity*price*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortMarketOrder", cfd, new MarketOrder(Symbols.DE10YBEUR, -quantity, time), -quantity*price*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdLongLimitOrder", cfd, new LimitOrder(Symbols.DE10YBEUR, quantity, priceMinusDelta, time), quantity*priceMinusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortLimit Order", cfd, new LimitOrder(Symbols.DE10YBEUR, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdLongStopLimitOrder", cfd, new StopLimitOrder(Symbols.DE10YBEUR, quantity,.5m*priceMinusDelta, priceMinusDelta, time), quantity*priceMinusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortStopLimitOrder", cfd, new StopLimitOrder(Symbols.DE10YBEUR, -quantity, 1.5m*pricePlusDelta, pricePlusDelta, time), -quantity*pricePlusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdLongStopMarketOrder", cfd, new StopMarketOrder(Symbols.DE10YBEUR, quantity, priceMinusDelta, time), quantity*priceMinusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdLongStopMarketOrder", cfd, new StopMarketOrder(Symbols.DE10YBEUR, quantity, pricePlusDelta, time), quantity*price*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortStopMarketOrder", cfd, new StopMarketOrder(Symbols.DE10YBEUR, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortStopMarketOrder", cfd, new StopMarketOrder(Symbols.DE10YBEUR, -quantity, priceMinusDelta, time), -quantity*price*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdLongTrailingStopOrderPriceMinusDelta", cfd, new TrailingStopOrder(Symbols.DE10YBEUR, quantity, priceMinusDelta, 0.1m, true, time), quantity*priceMinusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdLongTrailingStopOrderPricePlusDelta", cfd, new TrailingStopOrder(Symbols.DE10YBEUR, quantity, pricePlusDelta, 0.1m, true, time), quantity*price*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortTrailingStopOrderPricePlusDelta", cfd, new TrailingStopOrder(Symbols.DE10YBEUR, -quantity, pricePlusDelta, 0.1m, true, time), -quantity*pricePlusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortTrailingStopOrderPriceMinusDelta", cfd, new TrailingStopOrder(Symbols.DE10YBEUR, -quantity, priceMinusDelta, 0.1m, true, time), -quantity*price*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdShortLimitIfTouchedOrder", cfd, new LimitIfTouchedOrder(Symbols.DE10YBEUR, -quantity, 1.5m*pricePlusDelta, pricePlusDelta, time), -quantity*pricePlusDelta*multiplierTimesConversionRate),
|
|
new ValueTestParameters("CfdLongLimitIfTouchedOrder", cfd, new LimitIfTouchedOrder(Symbols.DE10YBEUR, quantity,.5m*priceMinusDelta, priceMinusDelta, time), quantity*priceMinusDelta*multiplierTimesConversionRate),
|
|
|
|
|
|
// equity/index option orders
|
|
new ValueTestParameters("OptionLongMarketOrder", option, new MarketOrder(Symbols.SPY_P_192_Feb19_2016, quantity, time), quantity*price),
|
|
new ValueTestParameters("OptionShortMarketOrder", option, new MarketOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, time), -quantity*price),
|
|
new ValueTestParameters("OptionLongLimitOrder", option, new LimitOrder(Symbols.SPY_P_192_Feb19_2016, quantity, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("OptionShortLimit Order", option, new LimitOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("OptionLongStopLimitOrder", option, new StopLimitOrder(Symbols.SPY_P_192_Feb19_2016, quantity,.5m*priceMinusDelta, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("OptionShortStopLimitOrder", option, new StopLimitOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, 1.5m*pricePlusDelta, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("OptionLongStopMarketOrder", option, new StopMarketOrder(Symbols.SPY_P_192_Feb19_2016, quantity, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("OptionLongStopMarketOrder", option, new StopMarketOrder(Symbols.SPY_P_192_Feb19_2016, quantity, pricePlusDelta, time), quantity*price),
|
|
new ValueTestParameters("OptionShortStopMarketOrder", option, new StopMarketOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("OptionShortStopMarketOrder", option, new StopMarketOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, priceMinusDelta, time), -quantity*price),
|
|
new ValueTestParameters("OptionLongTrailingStopOrdePriceMinusDeltar", option, new TrailingStopOrder(Symbols.SPY_P_192_Feb19_2016, quantity, priceMinusDelta, 0.1m, true, time), quantity*priceMinusDelta),
|
|
new ValueTestParameters("OptionLongTrailingStopOrderPricePlusDelta", option, new TrailingStopOrder(Symbols.SPY_P_192_Feb19_2016, quantity, pricePlusDelta, 0.1m, true, time), quantity*price),
|
|
new ValueTestParameters("OptionShortTrailingStopOrderPricePlusDelta", option, new TrailingStopOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, pricePlusDelta, 0.1m, true, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("OptionShortTrailingStopOrderPriceMinusDelta", option, new TrailingStopOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, priceMinusDelta, 0.1m, true, time), -quantity*price),
|
|
new ValueTestParameters("OptionShortLimitIfTouchedOrder", option, new LimitIfTouchedOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, 1.5m*pricePlusDelta, pricePlusDelta, time), -quantity*pricePlusDelta),
|
|
new ValueTestParameters("OptionLongLimitIfTouchedOrder", option, new LimitIfTouchedOrder(Symbols.SPY_P_192_Feb19_2016, quantity,.5m*priceMinusDelta, priceMinusDelta, time), quantity*priceMinusDelta),
|
|
|
|
new ValueTestParameters("OptionExerciseOrderPut", option, new OptionExerciseOrder(Symbols.SPY_P_192_Feb19_2016, quantity, time), quantity*option.Symbol.ID.StrikePrice),
|
|
new ValueTestParameters("OptionAssignmentOrderPut", option, new OptionExerciseOrder(Symbols.SPY_P_192_Feb19_2016, -quantity, time), -quantity*option.Symbol.ID.StrikePrice),
|
|
new ValueTestParameters("OptionExerciseOrderCall", option, new OptionExerciseOrder(Symbols.SPY_C_192_Feb19_2016, quantity, time), quantity*option.Symbol.ID.StrikePrice),
|
|
new ValueTestParameters("OptionAssignmentOrderCall", option, new OptionExerciseOrder(Symbols.SPY_C_192_Feb19_2016, -quantity, time), -quantity*option.Symbol.ID.StrikePrice),
|
|
|
|
|
|
}.Select(x => new TestCaseData(x).SetName(x.Name)).ToArray();
|
|
}
|
|
|
|
public class ValueTestParameters
|
|
{
|
|
public string Name { get; init; }
|
|
public Security Security { get; init; }
|
|
public Order Order { get; init; }
|
|
public decimal ExpectedValue { get; init; }
|
|
|
|
public ValueTestParameters(string name, Security security, Order order, decimal expectedValue)
|
|
{
|
|
Name = name;
|
|
Security = security;
|
|
Order = order;
|
|
ExpectedValue = expectedValue;
|
|
}
|
|
}
|
|
}
|
|
}
|