diff --git a/Common/Brokerages/PublicBrokerageModel.cs b/Common/Brokerages/PublicBrokerageModel.cs
index 34ce59eab824..63b13a08f6e4 100644
--- a/Common/Brokerages/PublicBrokerageModel.cs
+++ b/Common/Brokerages/PublicBrokerageModel.cs
@@ -96,6 +96,21 @@ public override bool CanSubmitOrder(Security security, Order order, out Brokerag
return false;
}
+ // Public.com only accepts Limit orders in the extended (outside regular trading hours) session.
+ if (order.Properties is PublicOrderProperties { OutsideRegularTradingHours: true } && order.Type != OrderType.Limit)
+ {
+ message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
+ Messages.PublicBrokerageModel.ExtendedMarketOrderMustBeLimit(order));
+ return false;
+ }
+
+ if (order.Properties is PublicOrderProperties publicOrderProperties)
+ {
+ // A cash account has no margin buying power, so margin is always off there.
+ // On a margin account, keep an explicit choice and otherwise use margin by default.
+ publicOrderProperties.UseMargin = AccountType != AccountType.Cash && (publicOrderProperties.UseMargin ?? true);
+ }
+
// Public.com handles crossing a zero position natively, so the order is not split or rejected here.
return base.CanSubmitOrder(security, order, out message);
}
diff --git a/Common/Messages/Messages.Brokerages.cs b/Common/Messages/Messages.Brokerages.cs
index dcc74d469230..fbdb8ee51da0 100644
--- a/Common/Messages/Messages.Brokerages.cs
+++ b/Common/Messages/Messages.Brokerages.cs
@@ -628,6 +628,21 @@ public static string MarketOrdersNotSupportedOutsideRegularTradingHours()
}
}
+ ///
+ /// Provides user-facing messages for the class and its consumers or related classes
+ ///
+ public static class PublicBrokerageModel
+ {
+ ///
+ /// Returns a message explaining that orders for the extended market must be Limit orders with Day time-in-force.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static string ExtendedMarketOrderMustBeLimit(Orders.Order order)
+ {
+ return Invariant($"Orders for extended market must be of type '{nameof(OrderType.Limit)}' and with 'DAY' time-in-force, but {order.Type} was specified.");
+ }
+ }
+
///
/// Provides user-facing messages for the class and its consumers or related classes
///
diff --git a/Common/Orders/PublicOrderProperties.cs b/Common/Orders/PublicOrderProperties.cs
new file mode 100644
index 000000000000..a962f62dc7c5
--- /dev/null
+++ b/Common/Orders/PublicOrderProperties.cs
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *
+*/
+
+namespace QuantConnect.Orders
+{
+ ///
+ /// Represents the properties of an order in Public.com.
+ ///
+ public class PublicOrderProperties : OrderProperties
+ {
+ ///
+ /// If set to true, allows the order to trigger or fill outside of regular trading hours
+ /// (the extended session).
+ ///
+ ///
+ /// Applicable to day time-in-force equity orders only.
+ ///
+ public bool OutsideRegularTradingHours { get; set; }
+
+ ///
+ /// Controls the buying power used by the order.
+ /// true uses margin buying power when the account allows it; false uses cash-only buying power.
+ /// When left null, the brokerage model fills it in from the account type before the order is sent.
+ ///
+ public bool? UseMargin { get; set; }
+ }
+}
diff --git a/Tests/Common/Brokerages/PublicBrokerageModelTests.cs b/Tests/Common/Brokerages/PublicBrokerageModelTests.cs
index 706b65e34fe0..87ee718cbdb9 100644
--- a/Tests/Common/Brokerages/PublicBrokerageModelTests.cs
+++ b/Tests/Common/Brokerages/PublicBrokerageModelTests.cs
@@ -119,6 +119,28 @@ public void CanSubmitCrossZeroOrderReturnsTrue(OrderType orderType, decimal hold
Assert.That(message, Is.Null);
}
+ // A margin account keeps an explicit UseMargin choice and otherwise uses margin by default;
+ // a cash account has no margin buying power, so UseMargin is always forced off there.
+ [TestCase(AccountType.Margin, null, true)]
+ [TestCase(AccountType.Margin, true, true)]
+ [TestCase(AccountType.Margin, false, false)]
+ [TestCase(AccountType.Cash, null, false)]
+ [TestCase(AccountType.Cash, true, false)]
+ [TestCase(AccountType.Cash, false, false)]
+ public void CanSubmitOrderResolvesUseMarginFromAccountType(AccountType accountType, bool? requestedUseMargin, bool expectedUseMargin)
+ {
+ var brokerageModel = new PublicBrokerageModel(accountType);
+ var security = GetSecurityForType(SecurityType.Equity);
+ var properties = new PublicOrderProperties { UseMargin = requestedUseMargin };
+ var order = new LimitOrder(security.Symbol, 1m, 100m, DateTime.UtcNow, properties: properties);
+
+ var canSubmit = brokerageModel.CanSubmitOrder(security, order, out var message);
+
+ Assert.That(canSubmit, Is.True);
+ Assert.That(message, Is.Null);
+ Assert.That(properties.UseMargin, Is.EqualTo(expectedUseMargin));
+ }
+
[Test]
public void CanUpdateOrderSingleOrderReturnsTrue()
{