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
15 changes: 15 additions & 0 deletions Common/Brokerages/PublicBrokerageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
15 changes: 15 additions & 0 deletions Common/Messages/Messages.Brokerages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,21 @@ public static string MarketOrdersNotSupportedOutsideRegularTradingHours()
}
}

/// <summary>
/// Provides user-facing messages for the <see cref="Brokerages.PublicBrokerageModel"/> class and its consumers or related classes
/// </summary>
public static class PublicBrokerageModel
{
/// <summary>
/// Returns a message explaining that orders for the extended market must be Limit orders with Day time-in-force.
/// </summary>
[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.");
}
}

/// <summary>
/// Provides user-facing messages for the <see cref="Brokerages.RBIBrokerageModel"/> class and its consumers or related classes
/// </summary>
Expand Down
40 changes: 40 additions & 0 deletions Common/Orders/PublicOrderProperties.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Represents the properties of an order in Public.com.
/// </summary>
public class PublicOrderProperties : OrderProperties
{
/// <summary>
/// If set to <c>true</c>, allows the order to trigger or fill outside of regular trading hours
/// (the extended session).
/// </summary>
/// <remarks>
/// Applicable to day time-in-force equity orders only.
/// </remarks>
public bool OutsideRegularTradingHours { get; set; }

/// <summary>
/// Controls the buying power used by the order.
/// <c>true</c> uses margin buying power when the account allows it; <c>false</c> uses cash-only buying power.
/// When left <c>null</c>, the brokerage model fills it in from the account type before the order is sent.
/// </summary>
public bool? UseMargin { get; set; }
}
}
22 changes: 22 additions & 0 deletions Tests/Common/Brokerages/PublicBrokerageModelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Loading