Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions Algorithm/QCAlgorithm.Trading.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public partial class QCAlgorithm
private bool _isMarketOnOpenOrderRestrictedForFuturesWarningSent;
private bool _isGtdTfiForMooAndMocOrdersValidationWarningSent;
private bool _isOptionsOrderOnStockSplitWarningSent;
private bool _liquidateSymbolNotFoundWarningSent;

/// <summary>
/// Transaction Manager - Process transaction fills and order management.
Expand Down Expand Up @@ -1268,7 +1269,7 @@ private OrderResponse PreOrderChecksImpl(SubmitOrderRequest request)
private Security GetSecurityForOrder(Symbol symbol)
{
var isCanonical = symbol.IsCanonical();
if (Securities.TryGetValue(symbol, out var security) &&
if (Securities.TryGetValue(symbol, out var security) &&
// Let canonical and delisted securities through instead of throwing. An invalid ticket will be returned later on when trying to submit the order.
(isCanonical || security.IsTradable || security.IsDelisted))
{
Expand Down Expand Up @@ -1326,8 +1327,7 @@ public List<OrderTicket> Liquidate(Symbol symbol = null, bool asynchronous = fal
IEnumerable<Symbol> toLiquidate;
if (symbol != null)
{
toLiquidate = Securities.ContainsKey(symbol)
? new[] { symbol } : Enumerable.Empty<Symbol>();
toLiquidate = new[] { symbol };
}
else
{
Expand Down Expand Up @@ -1357,6 +1357,17 @@ public List<OrderTicket> Liquidate(IEnumerable<Symbol> symbols, bool asynchronou
tag ??= "Liquidated";
foreach (var symbolToLiquidate in symbols)
{
// skip symbols that have not been added to the algorithm instead of throwing
if (!Securities.ContainsKey(symbolToLiquidate))
{
if (!_liquidateSymbolNotFoundWarningSent)
{
_liquidateSymbolNotFoundWarningSent = true;
Debug($"Warning: liquidate ignored symbol '{symbolToLiquidate}' because it has not been added to the algorithm. Add the security before liquidating it.");
}
continue;
}

// get open orders
var orders = Transactions.GetOpenOrders(symbolToLiquidate);

Expand Down
35 changes: 35 additions & 0 deletions Tests/Algorithm/AlgorithmTradingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1556,6 +1556,41 @@ public void LiquidateWorksAsExpected(Language language, bool? multipleSymbols, b
Assert.IsTrue(limitOrderCanceled);
}

[TestCase(Language.CSharp, true)]
[TestCase(Language.CSharp, false)]
[TestCase(Language.Python, true)]
[TestCase(Language.Python, false)]
public void LiquidateIgnoresSymbolsNotAddedToTheAlgorithm(Language language, bool singleSymbol)
{
var algo = GetAlgorithm(out _, 1, 0);

// AAPL was never added to the algorithm
List<OrderTicket> liquidatedTickets = null;
if (language == Language.CSharp)
{
if (singleSymbol)
{
Assert.DoesNotThrow(() => liquidatedTickets = algo.Liquidate(Symbols.AAPL));
}
else
{
Assert.DoesNotThrow(() => liquidatedTickets = algo.Liquidate(new List<Symbol>() { Symbols.AAPL }));
}
}
else
{
using (Py.GIL())
{
var symbols = singleSymbol
? Symbols.AAPL.ToPython()
: (new List<Symbol>() { Symbols.AAPL }).ToPython();
Assert.DoesNotThrow(() => liquidatedTickets = algo.Liquidate(symbols));
}
}

Assert.IsEmpty(liquidatedTickets);
}

[Test]
public void MarketOrdersAreSupportedForFuturesOnExtendedMarketHours()
{
Expand Down
Loading