diff --git a/Algorithm/QCAlgorithm.Trading.cs b/Algorithm/QCAlgorithm.Trading.cs
index dcd8262df3e9..db3610cd53af 100644
--- a/Algorithm/QCAlgorithm.Trading.cs
+++ b/Algorithm/QCAlgorithm.Trading.cs
@@ -1326,14 +1326,16 @@ public void SetMaximumOrders(int max)
/// True will liquidate existing holdings
/// Tag the order with a short string.
/// The order properties to use. Defaults to
+ /// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
- public void SetHoldings(List targets, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
+ public List SetHoldings(List targets, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
+ var orderTickets = new List();
//If they triggered a liquidate
if (liquidateExistingHoldings)
{
- Liquidate(GetSymbolsToLiquidate(targets.Select(t => t.Symbol)), tag: tag, orderProperties: orderProperties);
+ orderTickets = Liquidate(GetSymbolsToLiquidate(targets.Select(t => t.Symbol)), tag: tag, orderProperties: orderProperties);
}
foreach (var portfolioTarget in targets
@@ -1341,8 +1343,10 @@ public void SetHoldings(List targets, bool liquidateExistingHol
.Select(target => new PortfolioTarget(target.Symbol, CalculateOrderQuantity(target.Symbol, target.Quantity)))
.OrderTargetsByMarginImpact(this, targetIsDelta: true))
{
- SetHoldingsImpl(portfolioTarget.Symbol, portfolioTarget.Quantity, false, tag, orderProperties);
+ var tickets = SetHoldingsImpl(portfolioTarget.Symbol, portfolioTarget.Quantity, false, tag, orderProperties);
+ orderTickets.AddRange(tickets);
}
+ return orderTickets;
}
///
@@ -1353,11 +1357,12 @@ public void SetHoldings(List targets, bool liquidateExistingHol
/// liquidate existing holdings if necessary to hold this stock
/// Tag the order with a short string.
/// The order properties to use. Defaults to
+ /// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
- public void SetHoldings(Symbol symbol, double percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
+ public List SetHoldings(Symbol symbol, double percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
- SetHoldings(symbol, percentage.SafeDecimalCast(), liquidateExistingHoldings, tag, orderProperties);
+ return SetHoldings(symbol, percentage.SafeDecimalCast(), liquidateExistingHoldings, tag, orderProperties);
}
///
@@ -1368,11 +1373,12 @@ public void SetHoldings(Symbol symbol, double percentage, bool liquidateExisting
/// bool liquidate existing holdings if necessary to hold this stock
/// Tag the order with a short string.
/// The order properties to use. Defaults to
+ /// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
- public void SetHoldings(Symbol symbol, float percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
+ public List SetHoldings(Symbol symbol, float percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
- SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag, orderProperties);
+ return SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag, orderProperties);
}
///
@@ -1383,11 +1389,12 @@ public void SetHoldings(Symbol symbol, float percentage, bool liquidateExistingH
/// bool liquidate existing holdings if necessary to hold this stock
/// Tag the order with a short string.
/// The order properties to use. Defaults to
+ /// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
- public void SetHoldings(Symbol symbol, int percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
+ public List SetHoldings(Symbol symbol, int percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
- SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag, orderProperties);
+ return SetHoldings(symbol, (decimal)percentage, liquidateExistingHoldings, tag, orderProperties);
}
///
@@ -1401,22 +1408,24 @@ public void SetHoldings(Symbol symbol, int percentage, bool liquidateExistingHol
/// bool flag to clean all existing holdings before setting new faction.
/// Tag the order with a short string.
/// The order properties to use. Defaults to
+ /// A list of order tickets.
///
[DocumentationAttribute(TradingAndOrders)]
- public void SetHoldings(Symbol symbol, decimal percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
+ public List SetHoldings(Symbol symbol, decimal percentage, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
- SetHoldingsImpl(symbol, CalculateOrderQuantity(symbol, percentage), liquidateExistingHoldings, tag, orderProperties);
+ return SetHoldingsImpl(symbol, CalculateOrderQuantity(symbol, percentage), liquidateExistingHoldings, tag, orderProperties);
}
///
/// Set holdings implementation, which uses order quantities (delta) not percentage nor target final quantity
///
- private void SetHoldingsImpl(Symbol symbol, decimal orderQuantity, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
+ private List SetHoldingsImpl(Symbol symbol, decimal orderQuantity, bool liquidateExistingHoldings = false, string tag = null, IOrderProperties orderProperties = null)
{
+ var orderTickets = new List();
//If they triggered a liquidate
if (liquidateExistingHoldings)
{
- Liquidate(GetSymbolsToLiquidate([symbol]), tag: tag, orderProperties: orderProperties);
+ orderTickets = Liquidate(GetSymbolsToLiquidate([symbol]), tag: tag, orderProperties: orderProperties);
}
tag ??= "";
@@ -1435,19 +1444,22 @@ private void SetHoldingsImpl(Symbol symbol, decimal orderQuantity, bool liquidat
if (!Securities.TryGetValue(symbol, out security))
{
Error($"{symbol} not found in portfolio. Request this data when initializing the algorithm.");
- return;
+ return orderTickets;
}
//Check whether the exchange is open to send a market order. If not, send a market on open order instead
+ OrderTicket ticket;
if (security.Exchange.ExchangeOpen)
{
- MarketOrder(symbol, quantity, false, tag, orderProperties);
+ ticket = MarketOrder(symbol, quantity, false, tag, orderProperties);
}
else
{
- MarketOnOpenOrder(symbol, quantity, tag, orderProperties);
+ ticket = MarketOnOpenOrder(symbol, quantity, tag, orderProperties);
}
+ orderTickets.Add(ticket);
}
+ return orderTickets;
}
///
diff --git a/Tests/Algorithm/AlgorithmTradingTests.cs b/Tests/Algorithm/AlgorithmTradingTests.cs
index 1a3732a20327..f30ce4f969c9 100644
--- a/Tests/Algorithm/AlgorithmTradingTests.cs
+++ b/Tests/Algorithm/AlgorithmTradingTests.cs
@@ -31,6 +31,7 @@
using QuantConnect.Data;
using QuantConnect.Indicators;
using Python.Runtime;
+using QuantConnect.Algorithm.Framework.Portfolio;
namespace QuantConnect.Tests.Algorithm
{
@@ -1320,6 +1321,51 @@ public void SetHoldings_Long_ToZero_RoundOff()
// Assert.AreEqual(2500, actual);
//}
+ [TestCaseSource(nameof(SetHoldingReturnsOrderTicketsTestCases))]
+ public void TestSetHoldingReturnsOrderTickets(IEnumerable symbols, bool liquidateExistingHoldings, int expectedOrderTickets, string tag)
+ {
+ symbols = symbols.ToList();
+ // 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.Any())
+ {
+ orderTickets = algo.SetHoldings(symbols.First(), 1, liquidateExistingHoldings, tag);
+ }
+ else
+ {
+ orderTickets = algo.SetHoldings(new List(), liquidateExistingHoldings, tag);
+ }
+ }
+ // Assert the expected number of order tickets
+ Assert.AreEqual(expectedOrderTickets, orderTickets.Count);
+ }
+
[Test]
public void OrderQuantityConversionTest()
{
@@ -1821,5 +1867,14 @@ private bool HasSufficientBuyingPowerForOrder(decimal orderQuantity, Security se
new object[] { Language.CSharp, null, false, null },
new object[] { Language.Python, null, false, null }
};
+ private static object[] SetHoldingReturnsOrderTicketsTestCases =
+ {
+ new object[] { new List(), true, 3, "(Empty, true)"},
+ new object[] { new List(), false, 0, "(Empty, false)" },
+ new object[] { new List() { Symbols.IBM }, true, 3, "(OneSymbol, true)" },
+ new object[] { new List() { Symbols.IBM }, false, 1, "(OneSymbol, false)" },
+ new object[] { new List() { Symbols.AAPL, Symbols.SPY }, true, 3, "(MultipleSymbols, true)" },
+ new object[] { new List() { Symbols.AAPL, Symbols.SPY }, false, 2, "(MultipleSymbols, false)" },
+ };
}
}