Skip to content

Commit

Permalink
Feature: Implementation Charles Schwab Brokerage (#8406)
Browse files Browse the repository at this point in the history
* feat: ExtendedRegularTradingHours property in CharlesSchwabOrderProperties

* feat: BrokerageModel, FeeModel of Charles Schwab

* refactor: CharlesSchwab Fee model
remove: SecurityType.Index in CanSubmit CharlesSchwab

* refactor: CharlesSchwabFeeModel (a little bit simpler)

* refactor: CharlesSchwabFeeModel (more simpler)
  • Loading branch information
Romazes authored Nov 15, 2024
1 parent 725ad73 commit f7b012a
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 10 deletions.
7 changes: 6 additions & 1 deletion Common/Brokerages/BrokerageName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ public enum BrokerageName
/// <summary>
/// Transaction and submit/execution rules will use Terminal link models
/// </summary>
TerminalLink
TerminalLink,

/// <summary>
/// Transaction and submit/execution rules will use Charles Schwab models
/// </summary>
CharlesSchwab,
}
}
102 changes: 102 additions & 0 deletions Common/Brokerages/CharlesSchwabBrokerageModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* 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 QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Orders.Fees;
using System.Collections.Generic;

namespace QuantConnect.Brokerages
{
/// <summary>
/// Represents a brokerage model specific to Charles Schwab.
/// </summary>
public class CharlesSchwabBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// HashSet containing the security types supported by TradeStation.
/// </summary>
private readonly HashSet<SecurityType> _supportSecurityTypes = new(
new[]
{
SecurityType.Equity,
SecurityType.Option,
SecurityType.IndexOption
});

/// <summary>
/// HashSet containing the order types supported by the <see cref="CanSubmitOrder"/> operation in TradeStation.
/// </summary>
private readonly HashSet<OrderType> _supportOrderTypes = new(
new[]
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket
});

/// <summary>
/// Constructor for Charles Schwab brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public CharlesSchwabBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}

/// <summary>
/// Provides TradeStation fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>TradeStation fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new CharlesSchwabFeeModel();
}

/// <summary>
/// Returns true if the brokerage could accept this order. This takes into account
/// order type, security type, and order size limits.
/// </summary>
/// <remarks>
/// For example, a brokerage may have no connectivity at certain times, or an order rate/size limit
/// </remarks>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be processed</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be submitted</param>
/// <returns>True if the brokerage could process the order, false otherwise</returns>
public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
{
message = default;

if (!_supportSecurityTypes.Contains(security.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));

return false;
}

if (!_supportOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportOrderTypes));
return false;
}

return base.CanSubmitOrder(security, order, out message);
}
}
}
6 changes: 6 additions & 0 deletions Common/Brokerages/IBrokerageModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,9 @@ public static IBrokerageModel Create(IOrderProvider orderProvider, BrokerageName
case BrokerageName.TradeStation:
return new TradeStationBrokerageModel(accountType);

case BrokerageName.CharlesSchwab:
return new CharlesSchwabBrokerageModel(accountType);

default:
throw new ArgumentOutOfRangeException(nameof(brokerage), brokerage, null);
}
Expand Down Expand Up @@ -376,6 +379,9 @@ public static BrokerageName GetBrokerageName(IBrokerageModel brokerageModel)
case TradeStationBrokerageModel _:
return BrokerageName.TradeStation;

case CharlesSchwabBrokerageModel:
return BrokerageName.CharlesSchwab;

case DefaultBrokerageModel _:
return BrokerageName.Default;

Expand Down
32 changes: 32 additions & 0 deletions Common/Orders/CharlesSchwabOrderProperties.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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>
/// Contains additional properties and settings for an order submitted to Charles Schwab brokerage
/// </summary>
public class CharlesSchwabOrderProperties : OrderProperties
{
/// <summary>
/// If set to true, allows orders to also trigger or fill outside of regular trading hours.
/// </summary>
/// <remarks>
/// Schwab offers extended hours trading for stocks and ETFs during the business week.
/// Stock trading does not occur on weekends, holidays, or on days the market is closed.
/// </remarks>
public bool ExtendedRegularTradingHours { get; set; }
}
}
64 changes: 64 additions & 0 deletions Common/Orders/Fees/CharlesSchwabFeeModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* 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 QuantConnect.Securities;

namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a fee model specific to Charles Schwab.
/// </summary>
/// <see href="https://www.schwab.com/pricing"/>
public class CharlesSchwabFeeModel : FeeModel
{
/// <summary>
/// The exchange processing fee for standard option securities.
/// </summary>
private const decimal _optionIndexFee = 1m;

/// <summary>
/// Represents the fee associated with equity options transactions (per contract).
/// </summary>
private const decimal _optionFee = 0.65m;

/// <summary>
/// Calculates the order fee based on the security type and order parameters.
/// </summary>
/// <param name="parameters">The parameters for the order fee calculation, which include security and order details.</param>
/// <returns>
/// An <see cref="OrderFee"/> instance representing the calculated order fee.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="parameters"/> is <c>null</c>.
/// </exception>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
if (parameters.Security.Type.IsOption())
{
var feeRate = parameters.Security.Type switch
{
SecurityType.IndexOption => _optionIndexFee,
SecurityType.Option => _optionFee,
_ => 0m
};
return new OrderFee(new CashAmount(parameters.Order.AbsoluteQuantity * feeRate, Currencies.USD));
}
return OrderFee.Zero;
}
}
}
24 changes: 15 additions & 9 deletions Launcher/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,6 @@
"wolverine-on-behalf-of-comp-id": "",
"wolverine-log-fix-messages": false,

// TDAmeritrade configuration
"tdameritrade-account-number": "",
"tdameritrade-api-key": "",
"tdameritrade-access-token": "",

// RBI configuration
"rbi-host": "",
"rbi-port": "",
Expand Down Expand Up @@ -222,6 +217,17 @@
"alpaca-access-token": "",
"alpaca-paper-trading": true,

// Charles Schwab configuration
"charles-schwab-api-url": "https://api.schwabapi.com",
"charles-schwab-app-key": "",
"charles-schwab-secret": "",
"charles-schwab-account-number": "",
// Case 1 (normal)
"charles-schwab-refresh-token": "",
// Case 2 (developing)
"charles-schwab-authorization-code-from-url": "",
"charles-schwab-redirect-url": "",

// Exante trading configuration
// client-id, application-id, shared-key are required to access Exante REST API
// Exante generates them at https://exante.eu/clientsarea/dashboard/ after adding an application
Expand Down Expand Up @@ -661,18 +667,18 @@
"transaction-handler": "QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler"
},

"live-tdameritrade": {
"live-charlesschwab": {
"live-mode": true,

// real brokerage implementations require the BrokerageTransactionHandler
"live-mode-brokerage": "TDAmeritradeBrokerage",
"history-provider": [ "BrokerageHistoryProvider", "SubscriptionDataReaderHistoryProvider" ],
"live-mode-brokerage": "CharlesSchwabBrokerage",
"data-queue-handler": [ "CharlesSchwabBrokerage" ],
"setup-handler": "QuantConnect.Lean.Engine.Setup.BrokerageSetupHandler",
"result-handler": "QuantConnect.Lean.Engine.Results.LiveTradingResultHandler",
"data-feed-handler": "QuantConnect.Lean.Engine.DataFeeds.LiveTradingDataFeed",
"real-time-handler": "QuantConnect.Lean.Engine.RealTime.LiveTradingRealTimeHandler",
"transaction-handler": "QuantConnect.Lean.Engine.TransactionHandlers.BrokerageTransactionHandler",
"data-queue-handler": [ "TDAmeritradeBrokerage" ]
"history-provider": [ "BrokerageHistoryProvider", "SubscriptionDataReaderHistoryProvider" ]
},

"live-rbi": {
Expand Down

0 comments on commit f7b012a

Please sign in to comment.