chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
+148
View File
@@ -0,0 +1,148 @@
/*
* 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.Orders;
using QuantConnect.Securities;
using QuantConnect.Orders.Fees;
using System.Collections.Generic;
using QuantConnect.Orders.TimeInForces;
using System.Linq;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides an implementation of the <see cref="DefaultBrokerageModel"/> specific to Alpaca brokerage.
/// </summary>
public class AlpacaBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// The default start time of the <see cref="OrderType.MarketOnOpen"/> order submission window.
/// Example: 19:00 (7:00 PM).
/// </summary>
private static readonly TimeOnly _mooWindowStart = new(19, 0, 0);
/// <summary>
/// A dictionary that maps each supported <see cref="SecurityType"/> to an array of <see cref="OrderType"/> supported by Alpaca brokerage.
/// </summary>
private readonly Dictionary<SecurityType, HashSet<OrderType>> _supportOrderTypeBySecurityType = new()
{
{ SecurityType.Equity, new HashSet<OrderType> { OrderType.Market, OrderType.Limit, OrderType.StopMarket, OrderType.StopLimit,
OrderType.TrailingStop, OrderType.MarketOnOpen, OrderType.MarketOnClose } },
// Market and limit order types see https://docs.alpaca.markets/docs/options-trading-overview
{ SecurityType.Option, new HashSet<OrderType> { OrderType.Market, OrderType.Limit } },
{ SecurityType.Crypto, new HashSet<OrderType> { OrderType.Market, OrderType.Limit, OrderType.StopLimit }}
};
/// <summary>
/// Defines the default set of <see cref="SecurityType"/> values that support <see cref="OrderType.MarketOnOpen"/> orders.
/// </summary>
private readonly IReadOnlySet<SecurityType> _defaultMarketOnOpenSupportedSecurityTypes;
/// <summary>
/// Initializes a new instance of the <see cref="AlpacaBrokerageModel"/> class
/// </summary>
/// <remarks>All Alpaca accounts are set up as margin accounts</remarks>
public AlpacaBrokerageModel() : base(AccountType.Margin)
{
_defaultMarketOnOpenSupportedSecurityTypes = _supportOrderTypeBySecurityType.Where(x => x.Value.Contains(OrderType.MarketOnOpen)).Select(x => x.Key).ToHashSet();
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new AlpacaFeeModel();
}
/// <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 being ordered</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)
{
if (!_supportOrderTypeBySecurityType.TryGetValue(security.Type, out var supportOrderTypes))
{
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;
}
var supportsOutsideTradingHours = (order.Properties as AlpacaOrderProperties)?.OutsideRegularTradingHours ?? false;
if (supportsOutsideTradingHours && (order.Type != OrderType.Limit || order.TimeInForce is not DayTimeInForce))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.AlpacaBrokerageModel.TradingOutsideRegularHoursNotSupported(this, order.Type, order.TimeInForce));
return false;
}
if (!BrokerageExtensions.ValidateCrossZeroOrder(this, security, order, out message))
{
return false;
}
if (!BrokerageExtensions.ValidateMarketOnOpenOrder(security, order, GetMarketOnOpenAllowedWindow, _defaultMarketOnOpenSupportedSecurityTypes, out message))
{
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested updated to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
/// <summary>
/// Returns the allowed Market-on-Open submission window for Alpaca.
/// </summary>
/// <param name="marketHours">The market hours segment for the security.</param>
/// <returns>
/// A tuple with <c>MarketOnOpenWindowStart</c> (default 19:00 / 7:00 PM) and
/// <c>MarketOnOpenWindowEnd</c>, adjusted slightly before the market open to avoid rejection.
/// </returns>
private (TimeOnly MarketOnOpenWindowStart, TimeOnly MarketOnOpenWindowEnd) GetMarketOnOpenAllowedWindow(MarketHoursSegment marketHours)
{
return (_mooWindowStart, TimeOnly.FromTimeSpan(marketHours.Start.Add(-TimeSpan.FromMinutes(2))));
}
}
}
@@ -0,0 +1,80 @@
/*
* 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.Orders.Fees;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides properties specific to Alpha Streams
/// </summary>
public class AlphaStreamsBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Initializes a new instance of the <see cref="AlphaStreamsBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Margin"/> does not accept <see cref="AccountType.Cash"/>.</param>
public AlphaStreamsBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
if (accountType == AccountType.Cash)
{
throw new ArgumentException(Messages.AlphaStreamsBrokerageModel.UnsupportedAccountType, nameof(accountType));
}
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security) => new AlphaStreamsFeeModel();
/// <summary>
/// Gets a new slippage model that represents this brokerage's fill slippage behavior
/// </summary>
/// <param name="security">The security to get a slippage model for</param>
/// <returns>The new slippage model for this brokerage</returns>
public override ISlippageModel GetSlippageModel(Security security) => new AlphaStreamsSlippageModel();
/// <summary>
/// Gets the brokerage's leverage for the specified security
/// </summary>
/// <param name="security">The security's whose leverage we seek</param>
/// <returns>The leverage for the specified security</returns>
public override decimal GetLeverage(Security security)
{
switch (security.Type)
{
case SecurityType.Forex:
case SecurityType.Cfd:
return 10m;
default:
return 1m;
}
}
/// <summary>
/// Gets a new settlement model for the security
/// </summary>
/// <param name="security">The security to get a settlement model for</param>
/// <returns>The settlement model for this brokerage</returns>
public override ISettlementModel GetSettlementModel(Security security) => new ImmediateSettlementModel();
}
}
@@ -0,0 +1,151 @@
/*
* 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.Linq;
using System.Collections.Generic;
using QuantConnect.Benchmarks;
using QuantConnect.Data.Shortable;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides the Axos clearing brokerage model specific properties
/// </summary>
public class AxosClearingBrokerageModel : DefaultBrokerageModel
{
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Limit,
OrderType.Market,
OrderType.MarketOnClose
};
/// <summary>
/// The default markets for Trading Technologies
/// </summary>
public new static readonly IReadOnlyDictionary<SecurityType, string> DefaultMarketMap = new Dictionary<SecurityType, string>
{
{SecurityType.Equity, Market.USA}
}.ToReadOnlyDictionary();
/// <summary>
/// Creates a new instance
/// </summary>
public AxosClearingBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets => DefaultMarketMap;
/// <summary>
/// Provides Axos fee model
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new AxosFeeModel();
}
/// <summary>
/// Gets the shortable provider
/// </summary>
/// <returns>Shortable provider</returns>
public override IShortableProvider GetShortableProvider(Security security)
{
if(security.Type == SecurityType.Equity)
{
return new LocalDiskShortableProvider("axos");
}
return base.GetShortableProvider(security);
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
// Equivalent to no benchmark
return new FuncBenchmark(x => 0);
}
/// <summary>
/// Returns true if the brokerage could accept this order.
/// </summary>
/// <param name="security">The security being ordered</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 = null;
// validate security type
if (!DefaultMarketMap.ContainsKey(security.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
// validate order type
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
// validate orders quantity
if (order.AbsoluteQuantity % 1 != 0)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.AxosBrokerageModel.NonIntegerOrderQuantity(order));
return false;
}
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
}
}
+210
View File
@@ -0,0 +1,210 @@
/*
* 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.Linq;
using QuantConnect.Util;
using QuantConnect.Orders;
using QuantConnect.Benchmarks;
using QuantConnect.Securities;
using QuantConnect.Orders.Fees;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Binance specific properties
/// </summary>
public class BinanceBrokerageModel : DefaultBrokerageModel
{
private const decimal _defaultLeverage = 3;
private const decimal _defaultFutureLeverage = 25;
/// <summary>
/// The base Binance API endpoint URL.
/// </summary>
protected virtual string BaseApiEndpoint => "https://api.binance.com/api/v3";
/// <summary>
/// Market name
/// </summary>
protected virtual string MarketName => Market.Binance;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.Binance);
/// <summary>
/// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
public BinanceBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
{
}
/// <summary>
/// Binance global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
return security.Symbol.SecurityType == SecurityType.CryptoFuture ? _defaultFutureLeverage : _defaultLeverage;
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, MarketName);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Provides Binance fee model
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override IFeeModel GetFeeModel(Security security)
{
return new BinanceFeeModel();
}
/// <summary>
/// Binance does not support update of orders
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>Binance does not support update of orders, so it will always return false</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, 0, Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
return false;
}
/// <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 = null;
// Binance API provides minimum order size in quote currency
// and hence we have to check current order size using available price and order quantity
var quantityIsValid = true;
decimal price;
switch (order)
{
case LimitOrder limitOrder:
quantityIsValid &= IsOrderSizeLargeEnough(limitOrder.LimitPrice);
price = limitOrder.LimitPrice;
break;
case MarketOrder:
if (!security.HasData)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.NoDataForSymbol);
return false;
}
price = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
quantityIsValid &= IsOrderSizeLargeEnough(price);
break;
case StopLimitOrder stopLimitOrder:
price = stopLimitOrder.LimitPrice;
quantityIsValid &= IsOrderSizeLargeEnough(stopLimitOrder.LimitPrice);
if (!quantityIsValid)
{
break;
}
// Binance Trading UI requires this check too...
quantityIsValid &= IsOrderSizeLargeEnough(stopLimitOrder.StopPrice);
price = stopLimitOrder.StopPrice;
break;
case StopMarketOrder stopMarketOrder:
if (security.Symbol.SecurityType != SecurityType.CryptoFuture)
{
// despite Binance API allows you to post STOP_LOSS and TAKE_PROFIT order types
// they always fails with the content
// {"code":-1013,"msg":"Take profit orders are not supported for this symbol."}
// currently no symbols supporting TAKE_PROFIT or STOP_LOSS orders
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.BinanceBrokerageModel.UnsupportedOrderTypeWithLinkToSupportedTypes(BaseApiEndpoint, order, security));
return false;
}
quantityIsValid &= IsOrderSizeLargeEnough(stopMarketOrder.StopPrice);
price = stopMarketOrder.StopPrice;
break;
default:
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, new[] { OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit }));
return false;
}
if (!quantityIsValid)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.InvalidOrderSize(security, order.Quantity, price));
return false;
}
if (security.Type != SecurityType.Crypto && security.Type != SecurityType.CryptoFuture)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
return base.CanSubmitOrder(security, order, out message);
bool IsOrderSizeLargeEnough(decimal price) =>
// if we have a minimum order size we enforce it
!security.SymbolProperties.MinimumOrderSize.HasValue || order.AbsoluteQuantity * price > security.SymbolProperties.MinimumOrderSize;
}
/// <summary>
/// Returns a readonly dictionary of binance default markets
/// </summary>
protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Crypto] = marketName;
return map.ToReadOnlyDictionary();
}
}
}
@@ -0,0 +1,66 @@
/*
* 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.Benchmarks;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Securities.CryptoFuture;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Binance Coin Futures specific properties
/// </summary>
public class BinanceCoinFuturesBrokerageModel : BinanceFuturesBrokerageModel
{
/// <summary>
/// Creates a new instance
/// </summary>
public BinanceCoinFuturesBrokerageModel(AccountType accountType) : base(accountType)
{
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSD", SecurityType.CryptoFuture, MarketName);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Provides Binance Coin Futures fee model
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new BinanceCoinFuturesFeeModel();
}
/// <summary>
/// Creates the crypto future margin model for the given security
/// </summary>
/// <param name="security">The security to create the margin model for</param>
/// <returns>The margin model instance</returns>
protected override IBuyingPowerModel CreateCryptoFutureMarginModel(Security security)
{
return new CryptoFutureMarginModel(GetLeverage(security));
}
}
}
@@ -0,0 +1,103 @@
/*
* 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.Benchmarks;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Securities.CryptoFuture;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Binance Futures specific properties
/// </summary>
public class BinanceFuturesBrokerageModel : BinanceBrokerageModel
{
/// <summary>
/// Creates a new instance
/// </summary>
public BinanceFuturesBrokerageModel(AccountType accountType) : base(accountType)
{
if (accountType == AccountType.Cash)
{
throw new InvalidOperationException($"{SecurityType.CryptoFuture} can only be traded using a {AccountType.Margin} account type");
}
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSDT", SecurityType.CryptoFuture, MarketName);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Provides Binance Futures fee model
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new BinanceFuturesFeeModel();
}
/// <summary>
/// Gets a new margin interest rate model for the security
/// </summary>
/// <param name="security">The security to get a margin interest rate model for</param>
/// <returns>The margin interest rate model for this brokerage</returns>
public override IMarginInterestRateModel GetMarginInterestRateModel(Security security)
{
// only applies for perpetual futures
if (security.Symbol.SecurityType == SecurityType.CryptoFuture && security.Symbol.ID.Date == SecurityIdentifier.DefaultDate)
{
return new BinanceFutureMarginInterestRateModel();
}
return base.GetMarginInterestRateModel(security);
}
/// <summary>
/// Gets a new buying power model for the security.
/// For <see cref="SecurityType.CryptoFuture"/>, returns a <see cref="BinanceCryptoFutureMarginModel"/>
/// that recognizes supplementary stable coin collateral (e.g. BNFCR for EU Credits Trading Mode).
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
if (security?.Type == SecurityType.CryptoFuture)
{
return CreateCryptoFutureMarginModel(security);
}
return base.GetBuyingPowerModel(security);
}
/// <summary>
/// Creates the crypto future margin model for the given security
/// </summary>
/// <param name="security">The security to create the margin model for</param>
/// <returns>The margin model instance</returns>
protected virtual IBuyingPowerModel CreateCryptoFutureMarginModel(Security security)
{
return new BinanceCryptoFutureMarginModel(GetLeverage(security));
}
}
}
@@ -0,0 +1,65 @@
/*
* 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.Securities;
using System;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Binance.US specific properties
/// </summary>
public class BinanceUSBrokerageModel : BinanceBrokerageModel
{
/// <summary>
/// The base Binance Futures API endpoint URL.
/// </summary>
protected override string BaseApiEndpoint => "https://api.binance.us/api/v3";
/// <summary>
/// Market name
/// </summary>
protected override string MarketName => Market.BinanceUS;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.BinanceUS);
/// <summary>
/// Initializes a new instance of the <see cref="BinanceBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
public BinanceUSBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
{
if (accountType == AccountType.Margin)
{
throw new ArgumentException(Messages.BinanceUSBrokerageModel.UnsupportedAccountType);
}
}
/// <summary>
/// Binance global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
// margin trading is not currently supported by Binance.US
return 1m;
}
}
}
+165
View File
@@ -0,0 +1,165 @@
/*
* 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 System.Collections.Generic;
using System.Linq;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Bitfinex specific properties
/// </summary>
public class BitfinexBrokerageModel : DefaultBrokerageModel
{
private const decimal _maxLeverage = 3.3m;
/// <summary>
/// Represents a set of order types supported by the current brokerage model.
/// </summary>
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
};
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
/// <summary>
/// Initializes a new instance of the <see cref="BitfinexBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Margin"/></param>
public BitfinexBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <summary>
/// Bitfinex global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
if (security.Type == SecurityType.Crypto)
{
return _maxLeverage;
}
throw new ArgumentException(Messages.DefaultBrokerageModel.InvalidSecurityTypeForLeverage(security), nameof(security));
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSD", SecurityType.Crypto, Market.Bitfinex);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Provides Bitfinex fee model
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override IFeeModel GetFeeModel(Security security)
{
return new BitfinexFeeModel();
}
/// <summary>
/// Checks whether an order can be updated or not in the Bitfinex brokerage model
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The update request</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the update requested quantity is valid, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
// If the requested quantity is null is going to be ignored by the moment ApplyUpdateOrderRequest() method is call
if (request.Quantity == null)
{
message = null;
return true;
}
// Check if the requested quantity is valid
var requestedQuantity = (decimal)request.Quantity;
return IsValidOrderSize(security, requestedQuantity, out message);
}
/// <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)
{
if (!IsValidOrderSize(security, order.Quantity, out message))
{
return false;
}
message = null;
if (security.Type != SecurityType.Crypto)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets()
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Crypto] = Market.Bitfinex;
return map.ToReadOnlyDictionary();
}
}
}
@@ -0,0 +1,92 @@
/*
* 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 System.Collections.Generic;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Bloomberg FixNet HUB specific properties.
/// </summary>
public class BloombergFixBrokerageModel : DefaultBrokerageModel
{
private readonly HashSet<SecurityType> _supportedSecurityTypes = new()
{
SecurityType.Equity,
SecurityType.Option,
SecurityType.IndexOption,
SecurityType.Future,
};
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Market,
OrderType.MarketOnOpen,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
};
/// <summary>
/// Constructor for Bloomberg FIX brokerage model.
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public BloombergFixBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
if (accountType == AccountType.Cash)
{
throw new NotSupportedException($"Bloomberg FIX brokerage can only be used with a {AccountType.Margin} account type");
}
}
/// <summary>
/// Returns true if the brokerage could accept this order.
/// </summary>
/// <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>
public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
{
if (!_supportedSecurityTypes.Contains(security.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Bloomberg FixNet HUB supports cancel/replace (35=G) on order quantity, price, stop price,
/// order type and time in force.
/// </summary>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
}
}
+188
View File
@@ -0,0 +1,188 @@
/*
* 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 System.Linq;
using QuantConnect.Orders;
using QuantConnect.Securities;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides extension methods for handling brokerage operations.
/// </summary>
public static class BrokerageExtensions
{
/// <summary>
/// The default set of order types that are not allowed to cross zero holdings.
/// This is used by <see cref="ValidateCrossZeroOrder"/> when no custom set is provided.
/// </summary>
private static readonly IReadOnlySet<OrderType> DefaultNotSupportedCrossZeroOrderTypes = new HashSet<OrderType>
{
OrderType.MarketOnOpen,
OrderType.MarketOnClose
};
/// <summary>
/// Determines if executing the specified order will cross the zero holdings threshold.
/// </summary>
/// <param name="holdingQuantity">The current quantity of holdings.</param>
/// <param name="orderQuantity">The quantity of the order to be evaluated.</param>
/// <returns>
/// <c>true</c> if the order will change the holdings from positive to negative or vice versa; otherwise, <c>false</c>.
/// </returns>
/// <remarks>
/// This method checks if the order will result in a position change from positive to negative holdings or from negative to positive holdings.
/// </remarks>
public static bool OrderCrossesZero(decimal holdingQuantity, decimal orderQuantity)
{
//We're reducing position or flipping:
if (holdingQuantity > 0 && orderQuantity < 0)
{
if ((holdingQuantity + orderQuantity) < 0)
{
//We don't have enough holdings so will cross through zero:
return true;
}
}
else if (holdingQuantity < 0 && orderQuantity > 0)
{
if ((holdingQuantity + orderQuantity) > 0)
{
//Crossed zero: need to split into 2 orders:
return true;
}
}
return false;
}
/// <summary>
/// Determines whether an order that crosses zero holdings is permitted
/// for the specified brokerage model and order type.
/// </summary>
/// <param name="brokerageModel">The brokerage model performing the validation.</param>
/// <param name="security">The security associated with the order.</param>
/// <param name="order">The order to validate.</param>
/// <param name="notSupportedTypes">The set of order types that cannot cross zero holdings.</param>
/// <param name="message">
/// When the method returns <c>false</c>, contains a <see cref="BrokerageMessageEvent"/>
/// explaining why the order is not supported; otherwise <c>null</c>.
/// </param>
/// <returns>
/// <c>true</c> if the order is valid to submit; <c>false</c> if crossing zero is not supported
/// for the given order type.
/// </returns>
public static bool ValidateCrossZeroOrder(
IBrokerageModel brokerageModel,
Security security,
Order order,
out BrokerageMessageEvent message,
IReadOnlySet<OrderType> notSupportedTypes = null)
{
message = null;
notSupportedTypes ??= DefaultNotSupportedCrossZeroOrderTypes;
if (OrderCrossesZero(security.Holdings.Quantity, order.Quantity) && notSupportedTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(
BrokerageMessageType.Warning,
"NotSupported",
Messages.DefaultBrokerageModel.UnsupportedCrossZeroByOrderType(brokerageModel, order.Type)
);
return false;
}
return true;
}
/// <summary>
/// Validates whether a <see cref="OrderType.MarketOnOpen"/> order.
/// </summary>
/// <param name="security">The security associated with the order.</param>
/// <param name="order">The order to validate.</param>
/// <param name="getMarketOnOpenAllowedWindow">
/// A delegate that takes a <see cref="MarketHoursSegment"/> and returns the allowed
/// Market-on-Open submission window as a <see cref="TimeOnly"/> tuple (start, end).
/// </param>
/// <param name="supportedSecurityTypes"> The set of <see cref="SecurityType"/> values allowed for <see cref="OrderType.MarketOnOpen"/> orders.</param>
/// <param name="message">
/// An output <see cref="BrokerageMessageEvent"/> containing the reason
/// the order is invalid if the check fails; otherwise <c>null</c>.
/// </param>
/// <returns><c>true</c> if the order may be submitted within the given window; otherwise <c>false</c>.</returns>
public static bool ValidateMarketOnOpenOrder(
Security security,
Order order,
Func<MarketHoursSegment, (TimeOnly WindowStart, TimeOnly WindowEnd)> getMarketOnOpenAllowedWindow,
IReadOnlySet<SecurityType> supportedSecurityTypes,
out BrokerageMessageEvent message)
{
message = null;
if (order.Type != OrderType.MarketOnOpen)
{
return true;
}
if (!supportedSecurityTypes.Contains(security.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, $"UnsupportedSecurityType",
$"The broker does not support Market-on-Open orders for security type {security.Type}");
return false;
}
var targetTime = TimeOnly.FromDateTime(security.LocalTime);
var regularHours = security.Exchange.Hours.GetMarketHours(security.LocalTime).Segments.FirstOrDefault(x => x.State == MarketHoursState.Market);
var (windowStart, windowEnd) = (TimeOnly.MinValue, TimeOnly.MaxValue);
if (regularHours != null)
{
(windowStart, windowEnd) = getMarketOnOpenAllowedWindow(regularHours);
}
if (!targetTime.IsBetween(windowStart, windowEnd))
{
message = new BrokerageMessageEvent(
BrokerageMessageType.Warning,
"NotSupported",
Messages.DefaultBrokerageModel.UnsupportedMarketOnOpenOrderTime(windowStart, windowEnd)
);
return false;
}
return true;
}
/// <summary>
/// Gets the position that might result given the specified order direction and the current holdings quantity.
/// This is useful for brokerages that require more specific direction information than provided by the OrderDirection enum
/// (e.g. Tradier differentiates Buy/Sell and BuyToOpen/BuyToCover/SellShort/SellToClose)
/// </summary>
/// <param name="orderDirection">The order direction</param>
/// <param name="holdingsQuantity">The current holdings quantity</param>
/// <returns>The order position</returns>
public static OrderPosition GetOrderPosition(OrderDirection orderDirection, decimal holdingsQuantity)
{
return orderDirection switch
{
OrderDirection.Buy => holdingsQuantity >= 0 ? OrderPosition.BuyToOpen : OrderPosition.BuyToClose,
OrderDirection.Sell => holdingsQuantity <= 0 ? OrderPosition.SellToOpen : OrderPosition.SellToClose,
_ => throw new ArgumentOutOfRangeException(nameof(orderDirection), orderDirection, "Invalid order direction")
};
}
}
}
@@ -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.
*/
using System;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Represents the brokerage factory type required to load a data queue handler
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class BrokerageFactoryAttribute : Attribute
{
/// <summary>
/// The type of the brokerage factory
/// </summary>
public Type Type { get; set; }
/// <summary>
/// Creates a new instance of the <see cref="BrokerageFactoryAttribute"/> class
/// </summary>
/// <param name="type">The brokerage factory type</param>
public BrokerageFactoryAttribute(Type type)
{
Type = type;
}
}
}
@@ -0,0 +1,96 @@
/*
* 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.Brokerages
{
/// <summary>
/// Represents a message received from a brokerage
/// </summary>
public class BrokerageMessageEvent
{
/// <summary>
/// Gets the type of brokerage message
/// </summary>
public BrokerageMessageType Type { get; private set; }
/// <summary>
/// Gets the brokerage specific code for this message, zero if no code was specified
/// </summary>
public string Code { get; private set; }
/// <summary>
/// Gets the message text received from the brokerage
/// </summary>
public string Message { get; private set; }
/// <summary>
/// Initializes a new instance of the BrokerageMessageEvent class
/// </summary>
/// <param name="type">The type of brokerage message</param>
/// <param name="code">The brokerage specific code</param>
/// <param name="message">The message text received from the brokerage</param>
public BrokerageMessageEvent(BrokerageMessageType type, int code, string message)
{
Type = type;
Code = code.ToStringInvariant();
Message = message;
}
/// <summary>
/// Initializes a new instance of the BrokerageMessageEvent class
/// </summary>
/// <param name="type">The type of brokerage message</param>
/// <param name="code">The brokerage specific code</param>
/// <param name="message">The message text received from the brokerage</param>
public BrokerageMessageEvent(BrokerageMessageType type, string code, string message)
{
Type = type;
Code = code;
Message = message;
}
/// <summary>
/// Creates a new <see cref="BrokerageMessageEvent"/> to represent a disconnect message
/// </summary>
/// <param name="message">The message from the brokerage</param>
/// <returns>A brokerage disconnect message</returns>
public static BrokerageMessageEvent Disconnected(string message)
{
return new BrokerageMessageEvent(BrokerageMessageType.Disconnect, Messages.BrokerageMessageEvent.DisconnectCode, message);
}
/// <summary>
/// Creates a new <see cref="BrokerageMessageEvent"/> to represent a reconnect message
/// </summary>
/// <param name="message">The message from the brokerage</param>
/// <returns>A brokerage reconnect message</returns>
public static BrokerageMessageEvent Reconnected(string message)
{
return new BrokerageMessageEvent(BrokerageMessageType.Reconnect, Messages.BrokerageMessageEvent.ReconnectCode, message);
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>
/// A string that represents the current object.
/// </returns>
/// <filterpriority>2</filterpriority>
public override string ToString()
{
return Messages.BrokerageMessageEvent.ToString(this);
}
}
}
+53
View File
@@ -0,0 +1,53 @@
/*
* 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.Brokerages
{
/// <summary>
/// Specifies the type of message received from an IBrokerage implementation
/// </summary>
public enum BrokerageMessageType
{
/// <summary>
/// Informational message (0)
/// </summary>
Information,
/// <summary>
/// Warning message (1)
/// </summary>
Warning,
/// <summary>
/// Fatal error message, the algo will be stopped (2)
/// </summary>
Error,
/// <summary>
/// Brokerage reconnected with remote server (3)
/// </summary>
Reconnect,
/// <summary>
/// Brokerage disconnected from remote server (4)
/// </summary>
Disconnect,
/// <summary>
/// Action required by the user (5)
/// </summary>
ActionRequired,
}
}
+212
View File
@@ -0,0 +1,212 @@
/*
* 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;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Specifices what transaction model and submit/execution rules to use
/// </summary>
public enum BrokerageName
{
/// <summary>
/// Transaction and submit/execution rules will be the default as initialized
/// </summary>
Default,
/// <summary>
/// Transaction and submit/execution rules will be the default as initialized
/// Alternate naming for default brokerage
/// </summary>
QuantConnectBrokerage = Default,
/// <summary>
/// Transaction and submit/execution rules will use interactive brokers models
/// </summary>
InteractiveBrokersBrokerage,
/// <summary>
/// Transaction and submit/execution rules will use tradier models
/// </summary>
TradierBrokerage,
/// <summary>
/// Transaction and submit/execution rules will use oanda models
/// </summary>
OandaBrokerage,
/// <summary>
/// Transaction and submit/execution rules will use fxcm models
/// </summary>
FxcmBrokerage,
/// <summary>
/// Transaction and submit/execution rules will use bitfinex models
/// </summary>
Bitfinex,
/// <summary>
/// Transaction and submit/execution rules will use binance models
/// </summary>
Binance,
/// <summary>
/// Transaction and submit/execution rules will use gdax models
/// </summary>
[Obsolete("GDAX brokerage name is deprecated. Use Coinbase instead.")]
GDAX = 12,
/// <summary>
/// Transaction and submit/execution rules will use alpaca models
/// </summary>
Alpaca,
/// <summary>
/// Transaction and submit/execution rules will use AlphaStream models
/// </summary>
AlphaStreams,
/// <summary>
/// Transaction and submit/execution rules will use Zerodha models
/// </summary>
Zerodha,
/// <summary>
/// Transaction and submit/execution rules will use Samco models
/// </summary>
Samco,
/// <summary>
/// Transaction and submit/execution rules will use atreyu models
/// </summary>
Atreyu,
/// <summary>
/// Transaction and submit/execution rules will use TradingTechnologies models
/// </summary>
TradingTechnologies,
/// <summary>
/// Transaction and submit/execution rules will use Kraken models
/// </summary>
Kraken,
/// <summary>
/// Transaction and submit/execution rules will use ftx models
/// </summary>
FTX,
/// <summary>
/// Transaction and submit/execution rules will use ftx us models
/// </summary>
FTXUS,
/// <summary>
/// Transaction and submit/execution rules will use Exante models
/// </summary>
Exante,
/// <summary>
/// Transaction and submit/execution rules will use Binance.US models
/// </summary>
BinanceUS,
/// <summary>
/// Transaction and submit/execution rules will use Wolverine models
/// </summary>
Wolverine,
/// <summary>
/// Transaction and submit/execution rules will use TDameritrade models
/// </summary>
TDAmeritrade,
/// <summary>
/// Binance Futures USDⓈ-Margined contracts are settled and collateralized in their quote cryptocurrency, USDT or BUSD
/// </summary>
BinanceFutures,
/// <summary>
/// Binance Futures COIN-Margined contracts are settled and collateralized in their based cryptocurrency.
/// </summary>
BinanceCoinFutures,
/// <summary>
/// Transaction and submit/execution rules will use RBI models
/// </summary>
RBI,
/// <summary>
/// Transaction and submit/execution rules will use Bybit models
/// </summary>
Bybit,
/// <summary>
/// Transaction and submit/execution rules will use Eze models
/// </summary>
Eze,
/// <summary>
/// Transaction and submit/execution rules will use Axos models
/// </summary>
Axos,
/// <summary>
/// Transaction and submit/execution rules will use Coinbase broker's model
/// </summary>
Coinbase,
/// <summary>
/// Transaction and submit/execution rules will use TradeStation models
/// </summary>
TradeStation,
/// <summary>
/// Transaction and submit/execution rules will use Terminal link models
/// </summary>
TerminalLink,
/// <summary>
/// Transaction and submit/execution rules will use Charles Schwab models
/// </summary>
CharlesSchwab,
/// <summary>
/// Transaction and submit/execution rules will use Tastytrade models
/// </summary>
Tastytrade,
/// <summary>
/// Transaction and submit/execution rules will use interactive brokers Fix models
/// </summary>
InteractiveBrokersFix,
/// <summary>
/// Transaction and submit/execution rules will use dYdX models
/// </summary>
DYDX,
/// <summary>
/// Transaction and submit/execution rules will use Webull models
/// </summary>
Webull,
/// <summary>
/// Transaction and submit/execution rules will use Public.com models
/// </summary>
Public
}
}
+218
View File
@@ -0,0 +1,218 @@
/*
* 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 System.Collections.Generic;
using System.Linq;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Securities.CryptoFuture;
using QuantConnect.Util;
namespace QuantConnect.Brokerages;
/// <summary>
/// Provides Bybit specific properties
/// </summary>
public class BybitBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Market name
/// </summary>
protected virtual string MarketName => Market.Bybit;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.Bybit);
/// <summary>
/// Initializes a new instance of the <see cref="BybitBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Cash"/></param>
public BybitBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
{
}
/// <summary>
/// Bybit global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
return 10;
}
/// <summary>
/// Provides Bybit fee model
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override IFeeModel GetFeeModel(Security security)
{
return security.Type switch
{
SecurityType.Crypto => new BybitFeeModel(),
SecurityType.CryptoFuture => new BybitFuturesFeeModel(),
SecurityType.Base => base.GetFeeModel(security),
_ => throw new ArgumentOutOfRangeException(nameof(security), security, $"Not supported security type {security.Type}")
};
}
/// <summary>
/// Gets a new margin interest rate model for the security
/// </summary>
/// <param name="security">The security to get a margin interest rate model for</param>
/// <returns>The margin interest rate model for this brokerage</returns>
public override IMarginInterestRateModel GetMarginInterestRateModel(Security security)
{
// only applies for perpetual futures
if (security.Type == SecurityType.CryptoFuture &&
security.Symbol.ID.Date == SecurityIdentifier.DefaultDate)
{
return new BybitFutureMarginInterestRateModel();
}
return base.GetMarginInterestRateModel(security);
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSDC", SecurityType.Crypto, MarketName);
return SecurityBenchmark.CreateInstance(securities, symbol);
//todo default conversion?
}
/// <summary>
/// Returns true if the brokerage could accept this order update. This takes into account
/// order type, security type, and order size limits. Bybit can only update inverse, linear, and option orders
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage could update the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request,
out BrokerageMessageEvent message)
{
//can only update linear, inverse, and options
if (security.Type != SecurityType.CryptoFuture)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
return false;
}
if (order.Status is not (OrderStatus.New or OrderStatus.PartiallyFilled or OrderStatus.Submitted or OrderStatus.UpdateSubmitted))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
$"Order with status {order.Status} can't be modified");
return false;
}
if (request.Quantity.HasValue && !IsOrderSizeLargeEnough(security, Math.Abs(request.Quantity.Value)))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.InvalidOrderQuantity(security, request.Quantity.Value));
return false;
}
message = null;
return true;
}
/// <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)
{
if (security.Type != SecurityType.Crypto && security.Type != SecurityType.CryptoFuture && security.Type != SecurityType.Base)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
message = null;
bool quantityIsValid;
switch (order)
{
case StopLimitOrder:
case StopMarketOrder:
case LimitOrder:
case MarketOrder:
quantityIsValid = IsOrderSizeLargeEnough(security, Math.Abs(order.Quantity));
break;
default:
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order,
new[] { OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit }));
return false;
}
if (!quantityIsValid)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.InvalidOrderQuantity(security, order.Quantity));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Returns true if the order size is large enough for the given security.
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="orderQuantity">The order quantity</param>
/// <returns>True if the order size is large enough, false otherwise</returns>
protected virtual bool IsOrderSizeLargeEnough(Security security, decimal orderQuantity)
{
return !security.SymbolProperties.MinimumOrderSize.HasValue ||
orderQuantity >= security.SymbolProperties.MinimumOrderSize;
}
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Crypto] = marketName;
map[SecurityType.CryptoFuture] = marketName;
return map.ToReadOnlyDictionary();
}
}
@@ -0,0 +1,106 @@
/*
* 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 =
[
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.ComboMarket,
OrderType.ComboLimit,
OrderType.MarketOnClose,
OrderType.MarketOnOpen,
OrderType.StopLimit
];
/// <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);
}
}
}
+254
View File
@@ -0,0 +1,254 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014-2023 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 System.Linq;
using QuantConnect.Orders;
using QuantConnect.Securities;
using QuantConnect.Benchmarks;
using QuantConnect.Orders.Fees;
using System.Collections.Generic;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Represents a brokerage model for interacting with the Coinbase exchange.
/// This class extends the default brokerage model.
/// </summary>
public class CoinbaseBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Marks the end of stop market order support on Coinbase Pro.
/// For backtesting purposes, this field '_stopMarketOrderSupportEndDate' specifies the date when the
/// market structure update was applied, affecting the handling of historical data or simulations
/// involving stop market orders. Details: https://blog.coinbase.com/coinbase-pro-market-structure-update-fbd9d49f43d7
/// </summary>
private readonly DateTime _stopMarketOrderSupportEndDate = new DateTime(2019, 3, 23, 1, 0, 0);
/// <summary>
/// Notifies users that order updates are not supported by the current brokerage model.
/// </summary>
private readonly BrokerageMessageEvent _message = new(BrokerageMessageType.Warning, 0, Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
/// <summary>
/// Represents a set of order types supported by the current brokerage model.
/// </summary>
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Limit,
OrderType.Market,
OrderType.StopLimit,
OrderType.StopMarket
};
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets => GetDefaultMarkets(Market.Coinbase);
/// <summary>
/// Initializes a new instance of the <see cref="CoinbaseBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to <see cref="AccountType.Cash"/></param>
public CoinbaseBrokerageModel(AccountType accountType = AccountType.Cash)
: base(accountType)
{
if (accountType == AccountType.Margin)
{
throw new ArgumentException(Messages.CoinbaseBrokerageModel.UnsupportedAccountType, nameof(accountType));
}
}
/// <summary>
/// Coinbase global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
// margin trading is not currently supported by Coinbase
return 1m;
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSD", SecurityType.Crypto, Market.Coinbase);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Provides Coinbase fee model
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override IFeeModel GetFeeModel(Security security)
{
return new CoinbaseFeeModel();
}
/// <summary>
/// Determines whether the brokerage supports updating an existing order for the specified security.
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns><c>true</c> if the brokerage supports updating orders; otherwise, <c>false</c>.</returns>
/// <remarks>Coinbase: Only limit order types, with time in force type of good-till-cancelled can be edited.</remarks>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
if (order == null || security == null || request == null)
{
var parameter = order == null ? nameof(order) : nameof(security);
throw new ArgumentNullException(parameter, $"{parameter} parameter cannot be null. Please provide a valid {parameter} for submission.");
}
if (order.Type != OrderType.Limit)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
$"Order with type {order.Type} can't be modified, only LIMIT.");
return false;
}
if (order.TimeInForce != TimeInForce.GoodTilCanceled)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
$"Order's parameter 'TimeInForce' is not instance of Good Til Cancelled class.");
return false;
}
if (order.Status is not (OrderStatus.New or OrderStatus.PartiallyFilled or OrderStatus.Submitted or OrderStatus.UpdateSubmitted))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
$"Order with status {order.Status} can't be modified");
return false;
}
if (request.Quantity.HasValue && !IsOrderSizeLargeEnough(security, Math.Abs(request.Quantity.Value)))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.InvalidOrderQuantity(security, request.Quantity.Value));
return false;
}
message = null;
return true;
}
/// <summary>
/// Evaluates whether exchange will accept order. Will reject order update
/// </summary>
/// <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)
{
if(order == null || security == null)
{
var parameter = order == null ? nameof(order) : nameof(security);
throw new ArgumentNullException(parameter, $"{parameter} parameter cannot be null. Please provide a valid {parameter} for submission.");
}
if (order.BrokerId != null && order.BrokerId.Any())
{
message = _message;
return false;
}
if (!IsValidOrderSize(security, order.Quantity, out message))
{
return false;
}
if (security.Type != SecurityType.Crypto)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
if (order.Type == OrderType.StopMarket && order.Time >= _stopMarketOrderSupportEndDate)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.CoinbaseBrokerageModel.StopMarketOrdersNoLongerSupported(_stopMarketOrderSupportEndDate));
return false;
}
if (!IsOrderSizeLargeEnough(security, Math.Abs(order.Quantity)))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.InvalidOrderQuantity(security, order.Quantity));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
// margin trading is not currently supported by Coinbase
return new CashBuyingPowerModel();
}
/// <summary>
/// Returns true if the order size is large enough for the given security.
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="orderQuantity">The order quantity</param>
/// <returns>True if the order size is large enough, false otherwise</returns>
protected virtual bool IsOrderSizeLargeEnough(Security security, decimal orderQuantity)
{
#pragma warning disable CA1062
return !security!.SymbolProperties.MinimumOrderSize.HasValue ||
orderQuantity >= security.SymbolProperties.MinimumOrderSize;
#pragma warning restore CA1062
}
/// <summary>
/// Gets the default markets for different security types, with an option to override the market name for Crypto securities.
/// </summary>
/// <param name="marketName">The default market name for Crypto securities.</param>
/// <returns>
/// A read-only dictionary where the keys are <see cref="SecurityType"/> and the values are market names.
/// </returns>
protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Crypto] = marketName;
return map.ToReadOnlyDictionary();
}
}
}
@@ -0,0 +1,217 @@
/*
* 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 System.Linq;
using System.Threading;
using System.Threading.Tasks;
using QuantConnect.Data;
using QuantConnect.Interfaces;
using QuantConnect.Logging;
using QuantConnect.Packets;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides a default implementation o <see cref="IBrokerageMessageHandler"/> that will forward
/// messages as follows:
/// Information -> IResultHandler.Debug
/// Warning -> IResultHandler.Error &amp;&amp; IApi.SendUserEmail
/// Error -> IResultHandler.Error &amp;&amp; IAlgorithm.RunTimeError
/// </summary>
public class DefaultBrokerageMessageHandler : IBrokerageMessageHandler
{
private static readonly TimeSpan DefaultOpenThreshold = TimeSpan.FromMinutes(5);
private static readonly TimeSpan DefaultInitialDelay = TimeSpan.FromMinutes(15);
private volatile bool _connected;
private readonly IAlgorithm _algorithm;
private readonly TimeSpan _openThreshold;
private readonly TimeSpan _initialDelay;
private CancellationTokenSource _cancellationTokenSource;
private bool _outsideLeanOrderWarningEmitted;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBrokerageMessageHandler"/> class
/// </summary>
/// <param name="algorithm">The running algorithm</param>
/// <param name="initialDelay"></param>
/// <param name="openThreshold">Defines how long before market open to re-check for brokerage reconnect message</param>
public DefaultBrokerageMessageHandler(IAlgorithm algorithm, TimeSpan? initialDelay = null, TimeSpan? openThreshold = null)
: this(algorithm, null, null, initialDelay, openThreshold)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBrokerageMessageHandler"/> class
/// </summary>
/// <param name="algorithm">The running algorithm</param>
/// <param name="job">The job that produced the algorithm</param>
/// <param name="api">The api for the algorithm</param>
/// <param name="initialDelay"></param>
/// <param name="openThreshold">Defines how long before market open to re-check for brokerage reconnect message</param>
public DefaultBrokerageMessageHandler(IAlgorithm algorithm, AlgorithmNodePacket job, IApi api, TimeSpan? initialDelay = null, TimeSpan? openThreshold = null)
{
_algorithm = algorithm;
_connected = true;
_openThreshold = openThreshold ?? DefaultOpenThreshold;
_initialDelay = initialDelay ?? DefaultInitialDelay;
}
/// <summary>
/// Handles the message
/// </summary>
/// <param name="message">The message to be handled</param>
public void HandleMessage(BrokerageMessageEvent message)
{
// based on message type dispatch to result handler
switch (message.Type)
{
case BrokerageMessageType.Information:
_algorithm.Debug(Messages.DefaultBrokerageMessageHandler.BrokerageInfo(message));
break;
case BrokerageMessageType.Warning:
_algorithm.Error(Messages.DefaultBrokerageMessageHandler.BrokerageWarning(message));
break;
case BrokerageMessageType.Error:
// unexpected error, we need to close down shop
_algorithm.SetRuntimeError(new Exception(message.Message),
Messages.DefaultBrokerageMessageHandler.BrokerageErrorContext);
break;
case BrokerageMessageType.Disconnect:
_connected = false;
Log.Trace(Messages.DefaultBrokerageMessageHandler.Disconnected);
// check to see if any non-custom security exchanges are open within the next x minutes
var open = (from kvp in _algorithm.Securities
let security = kvp.Value
where security.Type != SecurityType.Base
let exchange = security.Exchange
let localTime = _algorithm.UtcTime.ConvertFromUtc(exchange.TimeZone)
where exchange.IsOpenDuringBar(
localTime,
localTime + _openThreshold,
_algorithm.SubscriptionManager.SubscriptionDataConfigService
.GetSubscriptionDataConfigs(security.Symbol)
.IsExtendedMarketHours())
select security).Any();
// if any are open then we need to kill the algorithm
if (open)
{
Log.Trace(Messages.DefaultBrokerageMessageHandler.DisconnectedWhenExchangesAreOpen(_initialDelay));
// wait 15 minutes before killing algorithm
StartCheckReconnected(_initialDelay, message);
}
else
{
Log.Trace(Messages.DefaultBrokerageMessageHandler.DisconnectedWhenExchangesAreClosed);
// if they aren't open, we'll need to check again a little bit before markets open
DateTime nextMarketOpenUtc;
if (_algorithm.Securities.Count != 0)
{
nextMarketOpenUtc = (from kvp in _algorithm.Securities
let security = kvp.Value
where security.Type != SecurityType.Base
let exchange = security.Exchange
let localTime = _algorithm.UtcTime.ConvertFromUtc(exchange.TimeZone)
let marketOpen = exchange.Hours.GetNextMarketOpen(localTime,
_algorithm.SubscriptionManager.SubscriptionDataConfigService
.GetSubscriptionDataConfigs(security.Symbol)
.IsExtendedMarketHours())
let marketOpenUtc = marketOpen.ConvertToUtc(exchange.TimeZone)
select marketOpenUtc).Min();
}
else
{
// if we have no securities just make next market open an hour from now
nextMarketOpenUtc = DateTime.UtcNow.AddHours(1);
}
var timeUntilNextMarketOpen = nextMarketOpenUtc - DateTime.UtcNow - _openThreshold;
Log.Trace(Messages.DefaultBrokerageMessageHandler.TimeUntilNextMarketOpen(timeUntilNextMarketOpen));
// wake up 5 minutes before market open and check if we've reconnected
StartCheckReconnected(timeUntilNextMarketOpen, message);
}
break;
case BrokerageMessageType.Reconnect:
_connected = true;
Log.Trace(Messages.DefaultBrokerageMessageHandler.Reconnected);
if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
{
_cancellationTokenSource.Cancel();
}
break;
case BrokerageMessageType.ActionRequired:
// not supported atm
_algorithm.SetRuntimeError(new Exception("Brokerage requires user action"), Messages.DefaultBrokerageMessageHandler.BrokerageDisconnectedShutDownContext);
break;
}
}
/// <summary>
/// Handles a new order placed manually in the brokerage side
/// </summary>
/// <param name="eventArgs">The new order event</param>
/// <returns>Whether the order should be added to the transaction handler</returns>
public virtual bool HandleOrder(NewBrokerageOrderNotificationEventArgs eventArgs)
{
if (!_outsideLeanOrderWarningEmitted)
{
_outsideLeanOrderWarningEmitted = true;
_algorithm.Error(Messages.DefaultBrokerageMessageHandler.IgnoreUnrecognizedOrder(eventArgs.Order.BrokerId.FirstOrDefault()));
}
return false;
}
private void StartCheckReconnected(TimeSpan delay, BrokerageMessageEvent message)
{
_cancellationTokenSource.DisposeSafely();
_cancellationTokenSource = new CancellationTokenSource(delay);
Task.Run(() =>
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
Thread.Sleep(TimeSpan.FromMinutes(1));
}
CheckReconnected(message);
}, _cancellationTokenSource.Token);
}
private void CheckReconnected(BrokerageMessageEvent message)
{
if (!_connected)
{
Log.Error(Messages.DefaultBrokerageMessageHandler.StillDisconnected);
_algorithm.SetRuntimeError(new Exception(message.Message),
Messages.DefaultBrokerageMessageHandler.BrokerageDisconnectedShutDownContext);
}
}
}
}
+407
View File
@@ -0,0 +1,407 @@
/*
* 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 System.Collections.Generic;
using QuantConnect.Benchmarks;
using QuantConnect.Data.Market;
using QuantConnect.Data.Shortable;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;
using QuantConnect.Securities.CryptoFuture;
using QuantConnect.Securities.Equity;
using QuantConnect.Securities.Future;
using QuantConnect.Securities.Option;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides a default implementation of <see cref="IBrokerageModel"/> that allows all orders and uses
/// the default transaction models
/// </summary>
public class DefaultBrokerageModel : IBrokerageModel
{
/// <summary>
/// The default markets for the backtesting brokerage
/// </summary>
public static readonly IReadOnlyDictionary<SecurityType, string> DefaultMarketMap = new Dictionary<SecurityType, string>
{
{SecurityType.Base, Market.USA},
{SecurityType.Equity, Market.USA},
{SecurityType.Option, Market.USA},
{SecurityType.Future, Market.CME},
{SecurityType.FutureOption, Market.CME},
{SecurityType.Forex, Market.Oanda},
{SecurityType.Cfd, Market.Oanda},
{SecurityType.Crypto, Market.Coinbase},
{SecurityType.CryptoFuture, Market.Binance},
{SecurityType.Index, Market.USA},
{SecurityType.IndexOption, Market.USA}
}.ToReadOnlyDictionary();
/// <summary>
/// Gets or sets the account type used by this model
/// </summary>
public virtual AccountType AccountType
{
get;
private set;
}
/// <summary>
/// Gets the brokerages model percentage factor used to determine the required unused buying power for the account.
/// From 1 to 0. Example: 0 means no unused buying power is required. 0.5 means 50% of the buying power should be left unused.
/// </summary>
public virtual decimal RequiredFreeBuyingPowerPercent => 0m;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public virtual IReadOnlyDictionary<SecurityType, string> DefaultMarkets
{
get { return DefaultMarketMap; }
}
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="QuantConnect.AccountType.Margin"/></param>
public DefaultBrokerageModel(AccountType accountType = AccountType.Margin)
{
AccountType = accountType;
}
/// <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 being ordered</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 virtual bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
{
if ((security.Type == SecurityType.Future || security.Type == SecurityType.FutureOption) && order.Type == OrderType.MarketOnOpen)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedMarketOnOpenOrdersForFuturesAndFutureOptions);
return false;
}
message = null;
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public virtual bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
/// <summary>
/// Returns true if the brokerage would be able to execute this order at this time assuming
/// market prices are sufficient for the fill to take place. This is used to emulate the
/// brokerage fills in backtesting and paper trading. For example some brokerages may not perform
/// executions during extended market hours. This is not intended to be checking whether or not
/// the exchange is open, that is handled in the Security.Exchange property.
/// </summary>
/// <param name="security">The security being traded</param>
/// <param name="order">The order to test for execution</param>
/// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
public virtual bool CanExecuteOrder(Security security, Order order)
{
return true;
}
/// <summary>
/// Applies the split to the specified order ticket
/// </summary>
/// <remarks>
/// This default implementation will update the orders to maintain a similar market value
/// </remarks>
/// <param name="tickets">The open tickets matching the split event</param>
/// <param name="split">The split event data</param>
public virtual void ApplySplit(List<OrderTicket> tickets, Split split)
{
// by default we'll just update the orders to have the same notional value
var splitFactor = split.SplitFactor;
tickets.ForEach(ticket => ticket.Update(new UpdateOrderFields
{
Quantity = (int?) (ticket.Quantity/splitFactor),
LimitPrice = ticket.OrderType.IsLimitOrder() ? ticket.Get(OrderField.LimitPrice)*splitFactor : (decimal?) null,
StopPrice = ticket.OrderType.IsStopOrder() ? ticket.Get(OrderField.StopPrice)*splitFactor : (decimal?) null,
TriggerPrice = ticket.OrderType == OrderType.LimitIfTouched ? ticket.Get(OrderField.TriggerPrice) * splitFactor : (decimal?) null,
TrailingAmount = ticket.OrderType == OrderType.TrailingStop && !ticket.Get<bool>(OrderField.TrailingAsPercentage) ? ticket.Get(OrderField.TrailingAmount) * splitFactor : (decimal?) null
}));
}
/// <summary>
/// Gets the brokerage's leverage for the specified security
/// </summary>
/// <param name="security">The security's whose leverage we seek</param>
/// <returns>The leverage for the specified security</returns>
public virtual decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash)
{
return 1m;
}
switch (security.Type)
{
case SecurityType.CryptoFuture:
return 25m;
case SecurityType.Equity:
return 2m;
case SecurityType.Forex:
case SecurityType.Cfd:
return 50m;
case SecurityType.Crypto:
return 1m;
case SecurityType.Base:
case SecurityType.Commodity:
case SecurityType.Option:
case SecurityType.FutureOption:
case SecurityType.Future:
case SecurityType.Index:
case SecurityType.IndexOption:
default:
return 1m;
}
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public virtual IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("SPY", SecurityType.Equity, Market.USA);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Gets a new fill model that represents this brokerage's fill behavior
/// </summary>
/// <param name="security">The security to get fill model for</param>
/// <returns>The new fill model for this brokerage</returns>
public virtual IFillModel GetFillModel(Security security)
{
switch (security.Type)
{
case SecurityType.Equity:
return new EquityFillModel();
case SecurityType.FutureOption:
return new FutureOptionFillModel();
case SecurityType.Future:
return new FutureFillModel();
case SecurityType.Base:
case SecurityType.Option:
case SecurityType.Commodity:
case SecurityType.Forex:
case SecurityType.Cfd:
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
case SecurityType.Index:
case SecurityType.IndexOption:
return new ImmediateFillModel();
default:
throw new ArgumentOutOfRangeException(Messages.DefaultBrokerageModel.InvalidSecurityTypeToGetFillModel(this, security));
}
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public virtual IFeeModel GetFeeModel(Security security)
{
switch (security.Type)
{
case SecurityType.Base:
case SecurityType.Forex:
case SecurityType.Cfd:
case SecurityType.Crypto:
case SecurityType.CryptoFuture:
case SecurityType.Index:
return new ConstantFeeModel(0m);
case SecurityType.Equity:
case SecurityType.Option:
case SecurityType.Future:
case SecurityType.FutureOption:
return new InteractiveBrokersFeeModel();
case SecurityType.Commodity:
default:
return new ConstantFeeModel(0m);
}
}
/// <summary>
/// Gets a new slippage model that represents this brokerage's fill slippage behavior
/// </summary>
/// <param name="security">The security to get a slippage model for</param>
/// <returns>The new slippage model for this brokerage</returns>
public virtual ISlippageModel GetSlippageModel(Security security)
{
return NullSlippageModel.Instance;
}
/// <summary>
/// Gets a new settlement model for the security
/// </summary>
/// <param name="security">The security to get a settlement model for</param>
/// <returns>The settlement model for this brokerage</returns>
public virtual ISettlementModel GetSettlementModel(Security security)
{
if (AccountType == AccountType.Cash)
{
switch (security.Type)
{
case SecurityType.Equity:
return new DelayedSettlementModel(Equity.DefaultSettlementDays, Equity.DefaultSettlementTime);
case SecurityType.Option:
return new DelayedSettlementModel(Option.DefaultSettlementDays, Option.DefaultSettlementTime);
}
}
if(security.Symbol.SecurityType == SecurityType.Future)
{
return new FutureSettlementModel();
}
return new ImmediateSettlementModel();
}
/// <summary>
/// Gets a new settlement model for the security
/// </summary>
/// <param name="security">The security to get a settlement model for</param>
/// <param name="accountType">The account type</param>
/// <returns>The settlement model for this brokerage</returns>
[Obsolete("Flagged deprecated and will remove December 1st 2018")]
public ISettlementModel GetSettlementModel(Security security, AccountType accountType)
{
return GetSettlementModel(security);
}
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public virtual IBuyingPowerModel GetBuyingPowerModel(Security security)
{
IBuyingPowerModel getCurrencyBuyingPowerModel() =>
AccountType == AccountType.Cash
? new CashBuyingPowerModel()
: new SecurityMarginModel(GetLeverage(security), RequiredFreeBuyingPowerPercent);
return security?.Type switch
{
SecurityType.Crypto => getCurrencyBuyingPowerModel(),
SecurityType.Forex => getCurrencyBuyingPowerModel(),
SecurityType.CryptoFuture => new CryptoFutureMarginModel(GetLeverage(security)),
SecurityType.Future => new FutureMarginModel(RequiredFreeBuyingPowerPercent, security),
SecurityType.FutureOption => new FuturesOptionsMarginModel(RequiredFreeBuyingPowerPercent, (Option)security),
SecurityType.IndexOption => new OptionMarginModel(RequiredFreeBuyingPowerPercent),
SecurityType.Option => new OptionMarginModel(RequiredFreeBuyingPowerPercent),
_ => new SecurityMarginModel(GetLeverage(security), RequiredFreeBuyingPowerPercent)
};
}
/// <summary>
/// Gets the shortable provider
/// </summary>
/// <returns>Shortable provider</returns>
public virtual IShortableProvider GetShortableProvider(Security security)
{
// Shortable provider, responsible for loading the data that indicates how much
// quantity we can short for a given asset. The NullShortableProvider default will
// allow for infinite quantities of any asset to be shorted.
return NullShortableProvider.Instance;
}
/// <summary>
/// Gets a new margin interest rate model for the security
/// </summary>
/// <param name="security">The security to get a margin interest rate model for</param>
/// <returns>The margin interest rate model for this brokerage</returns>
public virtual IMarginInterestRateModel GetMarginInterestRateModel(Security security)
{
return MarginInterestRateModel.Null;
}
/// <summary>
/// Gets a new buying power model for the security
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <param name="accountType">The account type</param>
/// <returns>The buying power model for this brokerage/security</returns>
[Obsolete("Flagged deprecated and will remove December 1st 2018")]
public IBuyingPowerModel GetBuyingPowerModel(Security security, AccountType accountType)
{
return GetBuyingPowerModel(security);
}
/// <summary>
/// Checks if the order quantity is valid, it means, the order size is bigger than the minimum size allowed
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="orderQuantity">The quantity of the order to be processed</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may be invalid</param>
/// <returns>True if the order quantity is bigger than the minimum allowed, false otherwise</returns>
public static bool IsValidOrderSize(Security security, decimal orderQuantity, out BrokerageMessageEvent message)
{
var minimumOrderSize = security.SymbolProperties.MinimumOrderSize;
if (minimumOrderSize != null && Math.Abs(orderQuantity) < minimumOrderSize)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.InvalidOrderQuantity(security, orderQuantity));
return false;
}
message = null;
return true;
}
}
}
@@ -0,0 +1,47 @@
/*
* 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.Interfaces;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Event arguments class for the <see cref="IBrokerage.DelistingNotification"/> event
/// </summary>
public class DelistingNotificationEventArgs
{
/// <summary>
/// Gets the option symbol which has received a notification
/// </summary>
public Symbol Symbol { get; }
/// <summary>
/// Initializes a new instance of the <see cref="DelistingNotificationEventArgs"/> class
/// </summary>
/// <param name="symbol">The symbol</param>
public DelistingNotificationEventArgs(Symbol symbol)
{
Symbol = symbol;
}
/// <summary>
/// Returns a string describing the delisting notification.
/// </summary>
public override string ToString()
{
return $"Symbol: {Symbol}";
}
}
}
@@ -0,0 +1,65 @@
/*
* 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.Linq;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides an implementation of <see cref="IBrokerageMessageHandler"/> that converts specified error codes into warnings
/// </summary>
public class DowngradeErrorCodeToWarningBrokerageMessageHandler : IBrokerageMessageHandler
{
private readonly HashSet<string> _errorCodesToIgnore;
private readonly IBrokerageMessageHandler _brokerageMessageHandler;
/// <summary>
/// Initializes a new instance of the <see cref="DowngradeErrorCodeToWarningBrokerageMessageHandler"/> class
/// </summary>
/// <param name="brokerageMessageHandler">The brokerage message handler to be wrapped</param>
/// <param name="errorCodesToIgnore">The error codes to convert to warning messages</param>
public DowngradeErrorCodeToWarningBrokerageMessageHandler(IBrokerageMessageHandler brokerageMessageHandler, string[] errorCodesToIgnore)
{
_brokerageMessageHandler = brokerageMessageHandler;
_errorCodesToIgnore = errorCodesToIgnore.ToHashSet();
}
/// <summary>
/// Handles the message
/// </summary>
/// <param name="message">The message to be handled</param>
public void HandleMessage(BrokerageMessageEvent message)
{
if (message.Type == BrokerageMessageType.Error && _errorCodesToIgnore.Contains(message.Code))
{
// rewrite the ignored message as a warning message
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, message.Code, message.Message);
}
_brokerageMessageHandler.HandleMessage(message);
}
/// <summary>
/// Handles a new order placed manually in the brokerage side
/// </summary>
/// <param name="eventArgs">The new order event</param>
/// <returns>Whether the order should be added to the transaction handler</returns>
public bool HandleOrder(NewBrokerageOrderNotificationEventArgs eventArgs)
{
return _brokerageMessageHandler.HandleOrder(eventArgs);
}
}
}
+122
View File
@@ -0,0 +1,122 @@
/*
* 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.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using static QuantConnect.Util.SecurityExtensions;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Exante Brokerage Model Implementation for Back Testing.
/// </summary>
public class ExanteBrokerageModel : DefaultBrokerageModel
{
private const decimal EquityLeverage = 1.2m;
/// <summary>
/// Constructor for Exante brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public ExanteBrokerageModel(AccountType accountType = AccountType.Cash)
: base(accountType)
{
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("SPY", SecurityType.Equity, Market.USA);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <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 being ordered</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 = null;
if (order == null)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.ExanteBrokerageModel.NullOrder);
return false;
}
if (order.Price == 0m)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.ExanteBrokerageModel.PriceNotSet);
return false;
}
if (security.Type != SecurityType.Forex &&
security.Type != SecurityType.Equity &&
security.Type != SecurityType.Index &&
security.Type != SecurityType.Option &&
security.Type != SecurityType.Future &&
security.Type != SecurityType.Cfd &&
security.Type != SecurityType.Crypto &&
security.Type != SecurityType.Index)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
return true;
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security) => new ExanteFeeModel();
/// <summary>
/// Exante global leverage rule
/// </summary>
/// <param name="security">The security's whose leverage we seek</param>
/// <returns>The leverage for the specified security</returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
return security.Type switch
{
SecurityType.Forex => 1.05m,
SecurityType.Equity => EquityLeverage,
_ => 1.0m,
};
}
}
}
+142
View File
@@ -0,0 +1,142 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2023 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.Orders.Fees;
using QuantConnect.Securities;
using System;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Eze specific properties
/// </summary>
public class EzeBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Array's Eze supports security types
/// </summary>
private readonly HashSet<SecurityType> _supportSecurityTypes = new(
new[]
{
SecurityType.Equity,
SecurityType.Option,
SecurityType.Future,
SecurityType.FutureOption,
SecurityType.Index,
SecurityType.IndexOption
});
/// <summary>
/// Array's Eze supports order types
/// </summary>
private readonly HashSet<OrderType> _supportOrderTypes = new(
new[]
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.MarketOnOpen,
OrderType.MarketOnClose,
});
/// <summary>
/// Constructor for Eze brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public EzeBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
if (accountType == AccountType.Cash)
{
throw new NotSupportedException($"Eze brokerage can only be used with a {AccountType.Margin} account type");
}
}
/// <summary>
/// Provides Eze fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>Eze Fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new EzeFeeModel();
}
/// <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)
{
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;
}
if (order.AbsoluteQuantity % 1 != 0)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
$"Order Quantity must be Integer, but provided {order.AbsoluteQuantity}.");
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Returns true if the brokerage could accept this order update. This takes into account
/// order type, security type, and order size limits.
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage could update the order, false otherwise</returns>
/// <remarks>
/// The Eze supports update:
/// - quantity <see cref="Order.Quantity"/>
/// - LimitPrice <see cref="LimitOrder.LimitPrice"/>
/// - StopPrice <see cref="StopLimitOrder.StopPrice"/>
/// - OrderType <seealso cref="OrderType"/>
/// - Time In Force <see cref="Order.TimeInForce"/>
/// </remarks>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
}
}
+199
View File
@@ -0,0 +1,199 @@
/*
* 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.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Util;
using System.Collections.Generic;
using System.Linq;
namespace QuantConnect.Brokerages
{
/// <summary>
/// FTX Brokerage model
/// </summary>
public class FTXBrokerageModel : DefaultBrokerageModel
{
private readonly HashSet<OrderType> _supportedOrderTypes = new HashSet<OrderType>
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit
};
private const decimal _defaultLeverage = 3m;
/// <summary>
/// market name
/// </summary>
protected virtual string MarketName => Market.FTX;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.FTX);
/// <summary>
/// Creates an instance of <see cref="FTXBrokerageModel"/> class
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public FTXBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <summary>
/// Gets the brokerage's leverage for the specified security
/// </summary>
/// <param name="security">The security's whose leverage we seek</param>
/// <returns>The leverage for the specified security</returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash)
{
return 1m;
}
return _defaultLeverage;
}
/// <summary>
/// Provides FTX fee model
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
=> new FTXFeeModel();
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSD", SecurityType.Crypto, MarketName);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <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)
{
if (!IsValidOrderSize(security, order.Quantity, out message))
{
return false;
}
message = null;
// validate order type
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
if (order.Type is OrderType.StopMarket or OrderType.StopLimit)
{
if (!security.HasData)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.NoDataForSymbol);
return false;
}
var stopPrice = (order as StopMarketOrder)?.StopPrice;
if (!stopPrice.HasValue)
{
stopPrice = (order as StopLimitOrder)?.StopPrice;
}
switch (order.Direction)
{
case OrderDirection.Sell:
if (stopPrice > security.BidPrice)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.FTXBrokerageModel.TriggerPriceTooHigh);
}
break;
case OrderDirection.Buy:
if (stopPrice < security.AskPrice)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.FTXBrokerageModel.TriggerPriceTooLow);
}
break;
}
if (message != null)
{
return false;
}
}
if (security.Type != SecurityType.Crypto)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Please note that the order's queue priority will be reset, and the order ID of the modified order will be different from that of the original order.
/// Also note: this is implemented as cancelling and replacing your order.
/// There's a chance that the order meant to be cancelled gets filled and its replacement still gets placed.
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, 0, Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
return false;
}
/// <summary>
/// Returns a readonly dictionary of FTX default markets
/// </summary>
protected static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string market)
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Crypto] = market;
return map.ToReadOnlyDictionary();
}
}
}
+53
View File
@@ -0,0 +1,53 @@
/*
* 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.Collections.Generic;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages
{
/// <summary>
/// FTX.US Brokerage model
/// </summary>
public class FTXUSBrokerageModel : FTXBrokerageModel
{
/// <summary>
/// Market name
/// </summary>
protected override string MarketName => Market.FTXUS;
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.FTXUS);
/// <summary>
/// Creates an instance of <see cref="FTXUSBrokerageModel"/> class
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public FTXUSBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <summary>
/// Provides FTX.US fee model
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
=> new FTXUSFeeModel();
}
}
+246
View File
@@ -0,0 +1,246 @@
/*
* 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 System.Collections.Generic;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides FXCM specific properties
/// </summary>
public class FxcmBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// The default markets for the fxcm brokerage
/// </summary>
public new static readonly IReadOnlyDictionary<SecurityType, string> DefaultMarketMap = new Dictionary<SecurityType, string>
{
{SecurityType.Base, Market.USA},
{SecurityType.Equity, Market.USA},
{SecurityType.Option, Market.USA},
{SecurityType.Forex, Market.FXCM},
{SecurityType.Cfd, Market.FXCM}
}.ToReadOnlyDictionary();
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Limit,
OrderType.Market,
OrderType.StopMarket
};
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets => DefaultMarketMap;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="AccountType.Margin"/></param>
public FxcmBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <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"></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 = null;
// validate security type
if (security.Type != SecurityType.Forex && security.Type != SecurityType.Cfd)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
// validate order type
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
// validate order quantity
if (order.Quantity % security.SymbolProperties.LotSize != 0)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.FxcmBrokerageModel.InvalidOrderQuantityForLotSize(security));
return false;
}
// validate stop/limit orders prices
var limit = order as LimitOrder;
if (limit != null)
{
return IsValidOrderPrices(security, OrderType.Limit, limit.Direction, security.Price, limit.LimitPrice, ref message);
}
var stopMarket = order as StopMarketOrder;
if (stopMarket != null)
{
return IsValidOrderPrices(security, OrderType.StopMarket, stopMarket.Direction, stopMarket.StopPrice, security.Price, ref message);
}
// validate time in force
if (order.TimeInForce != TimeInForce.GoodTilCanceled)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedTimeInForce(this, order));
return false;
}
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
// validate order quantity
if (request.Quantity != null && request.Quantity % security.SymbolProperties.LotSize != 0)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.FxcmBrokerageModel.InvalidOrderQuantityForLotSize(security));
return false;
}
// determine direction via the new, updated quantity
var newQuantity = request.Quantity ?? order.Quantity;
var direction = newQuantity > 0 ? OrderDirection.Buy : OrderDirection.Sell;
// use security.Price if null, allows to pass checks
var stopPrice = request.StopPrice ?? security.Price;
var limitPrice = request.LimitPrice ?? security.Price;
return IsValidOrderPrices(security, order.Type, direction, stopPrice, limitPrice, ref message);
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new FxcmFeeModel();
}
/// <summary>
/// Gets a new settlement model for the security
/// </summary>
/// <param name="security">The security to get a settlement model for</param>
/// <returns>The settlement model for this brokerage</returns>
public override ISettlementModel GetSettlementModel(Security security)
{
return security.Type == SecurityType.Cfd
? new AccountCurrencyImmediateSettlementModel() :
(ISettlementModel)new ImmediateSettlementModel();
}
/// <summary>
/// Validates limit/stopmarket order prices, pass security.Price for limit/stop if n/a
/// </summary>
private static bool IsValidOrderPrices(Security security, OrderType orderType, OrderDirection orderDirection, decimal stopPrice, decimal limitPrice, ref BrokerageMessageEvent message)
{
// validate order price
var invalidPrice = orderType == OrderType.Limit && orderDirection == OrderDirection.Buy && limitPrice > security.Price ||
orderType == OrderType.Limit && orderDirection == OrderDirection.Sell && limitPrice < security.Price ||
orderType == OrderType.StopMarket && orderDirection == OrderDirection.Buy && stopPrice < security.Price ||
orderType == OrderType.StopMarket && orderDirection == OrderDirection.Sell && stopPrice > security.Price;
if (invalidPrice)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.FxcmBrokerageModel.InvalidOrderPrice);
return false;
}
// Validate FXCM maximum distance for limit and stop orders:
// there are two different Max Limits, 15000 pips and 50% rule,
// whichever comes first (for most pairs, 50% rule comes first)
var maxDistance = Math.Min(
// MinimumPriceVariation is 1/10th of a pip
security.SymbolProperties.MinimumPriceVariation * 10 * 15000,
security.Price / 2);
var currentPrice = security.Price;
var minPrice = currentPrice - maxDistance;
var maxPrice = currentPrice + maxDistance;
var outOfRangePrice = orderType == OrderType.Limit && orderDirection == OrderDirection.Buy && limitPrice < minPrice ||
orderType == OrderType.Limit && orderDirection == OrderDirection.Sell && limitPrice > maxPrice ||
orderType == OrderType.StopMarket && orderDirection == OrderDirection.Buy && stopPrice > maxPrice ||
orderType == OrderType.StopMarket && orderDirection == OrderDirection.Sell && stopPrice < minPrice;
if (outOfRangePrice)
{
var orderPrice = orderType == OrderType.Limit ? limitPrice : stopPrice;
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.FxcmBrokerageModel.PriceOutOfRange(orderType, orderDirection, orderPrice, currentPrice));
return false;
}
return true;
}
}
}
+35
View File
@@ -0,0 +1,35 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014-2023 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;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides GDAX specific properties
/// </summary>
[Obsolete("GDAXBrokerageModel is deprecated. Use CoinbaseBrokerageModel instead.")]
public class GDAXBrokerageModel : CoinbaseBrokerageModel
{
/// <summary>
/// Initializes a new instance of the <see cref="GDAXBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="AccountType.Cash"/></param>
public GDAXBrokerageModel(AccountType accountType = AccountType.Cash)
: base(accountType)
{ }
}
}
@@ -0,0 +1,37 @@
/*
* 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.Brokerages
{
/// <summary>
/// Provides an plugin point to allow algorithms to directly handle the messages
/// that come from their brokerage
/// </summary>
public interface IBrokerageMessageHandler
{
/// <summary>
/// Handles the message
/// </summary>
/// <param name="message">The message to be handled</param>
void HandleMessage(BrokerageMessageEvent message);
/// <summary>
/// Handles a new order placed manually in the brokerage side
/// </summary>
/// <param name="eventArgs">The new order event</param>
/// <returns>Whether the order should be added to the transaction handler</returns>
bool HandleOrder(NewBrokerageOrderNotificationEventArgs eventArgs);
}
}
+419
View File
@@ -0,0 +1,419 @@
/*
* 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 System.Collections.Generic;
using QuantConnect.Benchmarks;
using QuantConnect.Data.Market;
using QuantConnect.Interfaces;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Fills;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;
using QuantConnect.Python;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Models brokerage transactions, fees, and order
/// </summary>
public interface IBrokerageModel
{
/// <summary>
/// Gets the account type used by this model
/// </summary>
AccountType AccountType
{
get;
}
/// <summary>
/// Gets the brokerages model percentage factor used to determine the required unused buying power for the account.
/// From 1 to 0. Example: 0 means no unused buying power is required. 0.5 means 50% of the buying power should be left unused.
/// </summary>
decimal RequiredFreeBuyingPowerPercent
{
get;
}
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; }
/// <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 being ordered</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>
bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message);
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested updated to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message);
/// <summary>
/// Returns true if the brokerage would be able to execute this order at this time assuming
/// market prices are sufficient for the fill to take place. This is used to emulate the
/// brokerage fills in backtesting and paper trading. For example some brokerages may not perform
/// executions during extended market hours. This is not intended to be checking whether or not
/// the exchange is open, that is handled in the Security.Exchange property.
/// </summary>
/// <param name="security">The security being ordered</param>
/// <param name="order">The order to test for execution</param>
/// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
bool CanExecuteOrder(Security security, Order order);
/// <summary>
/// Applies the split to the specified order ticket
/// </summary>
/// <param name="tickets">The open tickets matching the split event</param>
/// <param name="split">The split event data</param>
void ApplySplit(List<OrderTicket> tickets, Split split);
/// <summary>
/// Gets the brokerage's leverage for the specified security
/// </summary>
/// <param name="security">The security's whose leverage we seek</param>
/// <returns>The leverage for the specified security</returns>
decimal GetLeverage(Security security);
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
IBenchmark GetBenchmark(SecurityManager securities);
/// <summary>
/// Gets a new fill model that represents this brokerage's fill behavior
/// </summary>
/// <param name="security">The security to get fill model for</param>
/// <returns>The new fill model for this brokerage</returns>
IFillModel GetFillModel(Security security);
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
IFeeModel GetFeeModel(Security security);
/// <summary>
/// Gets a new slippage model that represents this brokerage's fill slippage behavior
/// </summary>
/// <param name="security">The security to get a slippage model for</param>
/// <returns>The new slippage model for this brokerage</returns>
ISlippageModel GetSlippageModel(Security security);
/// <summary>
/// Gets a new settlement model for the security
/// </summary>
/// <param name="security">The security to get a settlement model for</param>
/// <returns>The settlement model for this brokerage</returns>
ISettlementModel GetSettlementModel(Security security);
/// <summary>
/// Gets a new margin interest rate model for the security
/// </summary>
/// <param name="security">The security to get a margin interest rate model for</param>
/// <returns>The margin interest rate model for this brokerage</returns>
IMarginInterestRateModel GetMarginInterestRateModel(Security security);
/// <summary>
/// Gets a new settlement model for the security
/// </summary>
/// <param name="security">The security to get a settlement model for</param>
/// <param name="accountType">The account type</param>
/// <returns>The settlement model for this brokerage</returns>
[Obsolete("Flagged deprecated and will remove December 1st 2018")]
ISettlementModel GetSettlementModel(Security security, AccountType accountType);
/// <summary>
/// Gets a new buying power model for the security
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
IBuyingPowerModel GetBuyingPowerModel(Security security);
/// <summary>
/// Gets a new buying power model for the security
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <param name="accountType">The account type</param>
/// <returns>The buying power model for this brokerage/security</returns>
[Obsolete("Flagged deprecated and will remove December 1st 2018")]
IBuyingPowerModel GetBuyingPowerModel(Security security, AccountType accountType);
/// <summary>
/// Gets the shortable provider
/// </summary>
/// <returns>Shortable provider</returns>
IShortableProvider GetShortableProvider(Security security);
}
/// <summary>
/// Provides factory method for creating an <see cref="IBrokerageModel"/> from the <see cref="BrokerageName"/> enum
/// </summary>
public static class BrokerageModel
{
/// <summary>
/// Creates a new <see cref="IBrokerageModel"/> for the specified <see cref="BrokerageName"/>
/// </summary>
/// <param name="orderProvider">The order provider</param>
/// <param name="brokerage">The name of the brokerage</param>
/// <param name="accountType">The account type</param>
/// <returns>The model for the specified brokerage</returns>
public static IBrokerageModel Create(IOrderProvider orderProvider, BrokerageName brokerage, AccountType accountType)
{
switch (brokerage)
{
case BrokerageName.Default:
return new DefaultBrokerageModel(accountType);
case BrokerageName.TerminalLink:
return new TerminalLinkBrokerageModel(accountType);
case BrokerageName.Alpaca:
return new AlpacaBrokerageModel();
case BrokerageName.InteractiveBrokersBrokerage:
return new InteractiveBrokersBrokerageModel(accountType);
case BrokerageName.InteractiveBrokersFix:
return new InteractiveBrokersFixModel(accountType);
case BrokerageName.TradierBrokerage:
return new TradierBrokerageModel(accountType);
case BrokerageName.OandaBrokerage:
return new OandaBrokerageModel(accountType);
case BrokerageName.FxcmBrokerage:
return new FxcmBrokerageModel(accountType);
case BrokerageName.Bitfinex:
return new BitfinexBrokerageModel(accountType);
case BrokerageName.BinanceFutures:
return new BinanceFuturesBrokerageModel(accountType);
case BrokerageName.BinanceCoinFutures:
return new BinanceCoinFuturesBrokerageModel(accountType);
case BrokerageName.Binance:
return new BinanceBrokerageModel(accountType);
case BrokerageName.BinanceUS:
return new BinanceUSBrokerageModel(accountType);
case BrokerageName.GDAX:
return new GDAXBrokerageModel(accountType);
case BrokerageName.Coinbase:
return new CoinbaseBrokerageModel(accountType);
case BrokerageName.AlphaStreams:
return new AlphaStreamsBrokerageModel(accountType);
case BrokerageName.Zerodha:
return new ZerodhaBrokerageModel(accountType);
case BrokerageName.Axos:
return new AxosClearingBrokerageModel(accountType);
case BrokerageName.TradingTechnologies:
return new TradingTechnologiesBrokerageModel(accountType);
case BrokerageName.Samco:
return new SamcoBrokerageModel(accountType);
case BrokerageName.Kraken:
return new KrakenBrokerageModel(accountType);
case BrokerageName.Exante:
return new ExanteBrokerageModel(accountType);
case BrokerageName.FTX:
return new FTXBrokerageModel(accountType);
case BrokerageName.FTXUS:
return new FTXUSBrokerageModel(accountType);
case BrokerageName.Wolverine:
return new WolverineBrokerageModel(accountType);
case BrokerageName.TDAmeritrade:
return new TDAmeritradeBrokerageModel(accountType);
case BrokerageName.RBI:
return new RBIBrokerageModel(accountType);
case BrokerageName.Bybit:
return new BybitBrokerageModel(accountType);
case BrokerageName.Eze:
return new EzeBrokerageModel(accountType);
case BrokerageName.TradeStation:
return new TradeStationBrokerageModel(accountType);
case BrokerageName.CharlesSchwab:
return new CharlesSchwabBrokerageModel(accountType);
case BrokerageName.Tastytrade:
return new TastytradeBrokerageModel(accountType);
case BrokerageName.DYDX:
return new dYdXBrokerageModel(accountType);
case BrokerageName.Webull:
return new WebullBrokerageModel(accountType);
case BrokerageName.Public:
return new PublicBrokerageModel(accountType);
default:
throw new ArgumentOutOfRangeException(nameof(brokerage), brokerage, null);
}
}
/// <summary>
/// Gets the corresponding <see cref="BrokerageName"/> for the specified <see cref="IBrokerageModel"/>
/// </summary>
/// <param name="brokerageModel">The brokerage model</param>
/// <returns>The <see cref="BrokerageName"/> for the specified brokerage model</returns>
public static BrokerageName GetBrokerageName(IBrokerageModel brokerageModel)
{
var model = brokerageModel;
if (brokerageModel is BrokerageModelPythonWrapper)
{
model = (brokerageModel as BrokerageModelPythonWrapper).GetModel();
}
// Case order matters to ensure we get the correct brokerage name from the inheritance chain
switch (model)
{
case AlpacaBrokerageModel:
return BrokerageName.Alpaca;
case InteractiveBrokersBrokerageModel _:
return BrokerageName.InteractiveBrokersBrokerage;
case TradierBrokerageModel _:
return BrokerageName.TradierBrokerage;
case OandaBrokerageModel _:
return BrokerageName.OandaBrokerage;
case FxcmBrokerageModel _:
return BrokerageName.FxcmBrokerage;
case BitfinexBrokerageModel _:
return BrokerageName.Bitfinex;
case BinanceUSBrokerageModel _:
return BrokerageName.BinanceUS;
case BinanceBrokerageModel _:
return BrokerageName.Binance;
case GDAXBrokerageModel _:
return BrokerageName.GDAX;
case CoinbaseBrokerageModel _:
return BrokerageName.Coinbase;
case AlphaStreamsBrokerageModel _:
return BrokerageName.AlphaStreams;
case ZerodhaBrokerageModel _:
return BrokerageName.Zerodha;
case AxosClearingBrokerageModel _:
return BrokerageName.Axos;
case TradingTechnologiesBrokerageModel _:
return BrokerageName.TradingTechnologies;
case SamcoBrokerageModel _:
return BrokerageName.Samco;
case KrakenBrokerageModel _:
return BrokerageName.Kraken;
case ExanteBrokerageModel _:
return BrokerageName.Exante;
case FTXUSBrokerageModel _:
return BrokerageName.FTXUS;
case FTXBrokerageModel _:
return BrokerageName.FTX;
case WolverineBrokerageModel _:
return BrokerageName.Wolverine;
case TDAmeritradeBrokerageModel _:
return BrokerageName.TDAmeritrade;
case RBIBrokerageModel _:
return BrokerageName.RBI;
case BybitBrokerageModel _:
return BrokerageName.Bybit;
case EzeBrokerageModel _:
return BrokerageName.Eze;
case TradeStationBrokerageModel _:
return BrokerageName.TradeStation;
case CharlesSchwabBrokerageModel:
return BrokerageName.CharlesSchwab;
case TastytradeBrokerageModel:
return BrokerageName.Tastytrade;
case WebullBrokerageModel:
return BrokerageName.Webull;
case PublicBrokerageModel:
return BrokerageName.Public;
case DefaultBrokerageModel _:
return BrokerageName.Default;
default:
throw new ArgumentOutOfRangeException(nameof(brokerageModel), brokerageModel, null);
}
}
}
}
@@ -0,0 +1,357 @@
/*
* 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 System.Collections.Generic;
using System.Linq;
using QuantConnect.Util;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Securities;
using QuantConnect.Securities.Forex;
using QuantConnect.Securities.Option;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides properties specific to interactive brokers
/// </summary>
public class InteractiveBrokersBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Defines the default set of <see cref="SecurityType"/> values that support <see cref="OrderType.MarketOnOpen"/> orders.
/// </summary>
private static readonly IReadOnlySet<SecurityType> _defaultMarketOnOpenSupportedSecurityTypes = new HashSet<SecurityType>
{
SecurityType.Cfd,
SecurityType.Equity,
SecurityType.Option,
SecurityType.FutureOption,
SecurityType.IndexOption
};
/// <summary>
/// The default markets for the IB brokerage
/// </summary>
public new static readonly IReadOnlyDictionary<SecurityType, string> DefaultMarketMap = new Dictionary<SecurityType, string>
{
{SecurityType.Base, Market.USA},
{SecurityType.Equity, Market.USA},
{SecurityType.Index, Market.USA},
{SecurityType.Option, Market.USA},
{SecurityType.IndexOption, Market.USA},
{SecurityType.Future, Market.CME},
{SecurityType.FutureOption, Market.CME},
{SecurityType.Forex, Market.Oanda},
{SecurityType.Cfd, Market.InteractiveBrokers}
}.ToReadOnlyDictionary();
/// <summary>
/// Supported time in force
/// </summary>
protected virtual Type[] SupportedTimeInForces { get; } =
{
typeof(GoodTilCanceledTimeInForce),
typeof(DayTimeInForce),
typeof(GoodTilDateTimeInForce)
};
/// <summary>
/// Supported order types
/// </summary>
protected virtual HashSet<OrderType> SupportedOrderTypes { get; } = new HashSet<OrderType>
{
OrderType.Market,
OrderType.MarketOnOpen,
OrderType.MarketOnClose,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.TrailingStop,
OrderType.LimitIfTouched,
OrderType.ComboMarket,
OrderType.ComboLimit,
OrderType.ComboLegLimit,
OrderType.OptionExercise
};
/// <summary>
/// Initializes a new instance of the <see cref="InteractiveBrokersBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="AccountType.Margin"/></param>
public InteractiveBrokersBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets => DefaultMarketMap;
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
// Equivalent to no benchmark
return new FuncBenchmark(x => 0);
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new InteractiveBrokersFeeModel();
}
/// <summary>
/// Gets the brokerage's leverage for the specified security
/// </summary>
/// <param name="security">The security's whose leverage we seek</param>
/// <returns>The leverage for the specified security</returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash)
{
return 1m;
}
return security.Type == SecurityType.Cfd ? 10m : base.GetLeverage(security);
}
/// <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 being ordered</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 = null;
// validate order type
if (!SupportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, SupportedOrderTypes));
return false;
}
else if (order.Type == OrderType.MarketOnClose && security.Type != SecurityType.Future && security.Type != SecurityType.Equity && security.Type != SecurityType.Cfd)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, $"Unsupported order type for {security.Type} security type",
"InteractiveBrokers does not support Market-on-Close orders for other security types different than Future and Equity.");
return false;
}
else if (!BrokerageExtensions.ValidateMarketOnOpenOrder(security, order, GetMarketOnOpenAllowedWindow, _defaultMarketOnOpenSupportedSecurityTypes, out message))
{
return false;
}
if (order.Type == OrderType.ComboLegLimit && order.GroupOrderManager?.Count >= 4)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.InteractiveBrokersBrokerageModel.UnsupportedFourLegComboLegLimitOrders(this));
return false;
}
// validate security type
if (security.Type != SecurityType.Equity &&
security.Type != SecurityType.Forex &&
security.Type != SecurityType.Option &&
security.Type != SecurityType.Future &&
security.Type != SecurityType.FutureOption &&
security.Type != SecurityType.Index &&
security.Type != SecurityType.IndexOption &&
security.Type != SecurityType.Cfd)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
// validate order quantity
//https://www.interactivebrokers.com/en/?f=%2Fen%2Ftrading%2FforexOrderSize.php
if (security.Type == SecurityType.Forex &&
!IsForexWithinOrderSizeLimits(order.Symbol.Value, order.Quantity, out message))
{
return false;
}
// validate time in force
if (!SupportedTimeInForces.Contains(order.TimeInForce.GetType()))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedTimeInForce(this, order));
return false;
}
// IB doesn't support index options and cash-settled options exercise
if (order.Type == OrderType.OptionExercise &&
(security.Type == SecurityType.IndexOption ||
(security.Type == SecurityType.Option && (security as Option).ExerciseSettlement == SettlementType.Cash)))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.InteractiveBrokersBrokerageModel.UnsupportedExerciseForIndexAndCashSettledOptions(this, order));
return false;
}
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
if (order.SecurityType == SecurityType.Forex && request.Quantity != null)
{
return IsForexWithinOrderSizeLimits(order.Symbol.Value, request.Quantity.Value, out message);
}
return true;
}
/// <summary>
/// Returns true if the brokerage would be able to execute this order at this time assuming
/// market prices are sufficient for the fill to take place. This is used to emulate the
/// brokerage fills in backtesting and paper trading. For example some brokerages may not perform
/// executions during extended market hours. This is not intended to be checking whether or not
/// the exchange is open, that is handled in the Security.Exchange property.
/// </summary>
/// <param name="security"></param>
/// <param name="order">The order to test for execution</param>
/// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
public override bool CanExecuteOrder(Security security, Order order)
{
return order.SecurityType != SecurityType.Base;
}
/// <summary>
/// Returns true if the specified order is within IB's order size limits
/// </summary>
private bool IsForexWithinOrderSizeLimits(string currencyPair, decimal quantity, out BrokerageMessageEvent message)
{
/* https://www.interactivebrokers.com/en/trading/forexOrderSize.php
Currency Currency Description Minimum Order Size Maximum Order Size
USD US Dollar 25,000 7,000,000
AUD Australian Dollar 25,000 6,000,000
CAD Canadian Dollar 25,000 6,000,000
CHF Swiss Franc 25,000 6,000,000
CNH China Renminbi (offshore) 150,000 40,000,000
CZK Czech Koruna USD 25,000(1) USD 7,000,000(1)
DKK Danish Krone 150,000 35,000,000
EUR Euro 20,000 6,000,000
GBP British Pound Sterling 20,000 5,000,000
HKD Hong Kong Dollar 200,000 50,000,000
HUF Hungarian Forint USD 25,000(1) USD 7,000,000(1)
ILS Israeli Shekel USD 25,000(1) USD 7,000,000(1)
KRW Korean Won 0 200,000,000
JPY Japanese Yen 2,500,000 550,000,000
MXN Mexican Peso 300,000 70,000,000
NOK Norwegian Krone 150,000 35,000,000
NZD New Zealand Dollar 35,000 8,000,000
PLN Polish Zloty USD 25,000(1) USD 7,000,000(1)
RUB Russian Ruble 750,000 30,000,000
SEK Swedish Krona 175,000 40,000,000
SGD Singapore Dollar 35,000 8,000,000
ZAR South African Rand 350,000 100,000,000
*/
message = null;
// switch on the currency being bought
Forex.DecomposeCurrencyPair(currencyPair, out var baseCurrency, out _);
ForexCurrencyLimits.TryGetValue(baseCurrency, out var limits);
var min = limits?.Item1 ?? 0m;
var max = limits?.Item2 ?? 0m;
var absoluteQuantity = Math.Abs(quantity);
var orderIsWithinForexSizeLimits = ((min == 0 && absoluteQuantity > min) || (min > 0 && absoluteQuantity >= min)) && absoluteQuantity <= max;
if (!orderIsWithinForexSizeLimits)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "OrderSizeLimit",
Messages.InteractiveBrokersBrokerageModel.InvalidForexOrderSize(min, max, baseCurrency));
}
return orderIsWithinForexSizeLimits;
}
// currency -> (min, max)
private static readonly IReadOnlyDictionary<string, Tuple<decimal, decimal>> ForexCurrencyLimits =
new Dictionary<string, Tuple<decimal, decimal>>()
{
{"USD", Tuple.Create(25000m, 7000000m)},
{"AUD", Tuple.Create(25000m, 6000000m)},
{"CAD", Tuple.Create(25000m, 6000000m)},
{"CHF", Tuple.Create(25000m, 6000000m)},
{"CNH", Tuple.Create(150000m, 40000000m)},
{"CZK", Tuple.Create(0m, 0m)}, // need market price in USD or EUR -- do later when we support
{"DKK", Tuple.Create(150000m, 35000000m)},
{"EUR", Tuple.Create(20000m, 6000000m)},
{"GBP", Tuple.Create(20000m, 5000000m)},
{"HKD", Tuple.Create(200000m, 50000000m)},
{"HUF", Tuple.Create(0m, 0m)}, // need market price in USD or EUR -- do later when we support
{"ILS", Tuple.Create(0m, 0m)}, // need market price in USD or EUR -- do later when we support
{"KRW", Tuple.Create(0m, 200000000m)},
{"JPY", Tuple.Create(2500000m, 550000000m)},
{"MXN", Tuple.Create(300000m, 70000000m)},
{"NOK", Tuple.Create(150000m, 35000000m)},
{"NZD", Tuple.Create(35000m, 8000000m)},
{"PLN", Tuple.Create(0m, 0m)}, // need market price in USD or EUR -- do later when we support
{"RUB", Tuple.Create(750000m, 30000000m)},
{"SEK", Tuple.Create(175000m, 40000000m)},
{"SGD", Tuple.Create(35000m, 8000000m)},
{"ZAR", Tuple.Create(350000m, 100000000m)}
};
/// <summary>
/// Returns the allowed Market-on-Open submission window for a <see cref="MarketHoursSegment"/>.
/// </summary>
/// <param name="marketHours">The market hours segment for the security.</param>
/// <returns>
/// A tuple with <c>MarketOnOpenWindowStart</c> and <c>MarketOnOpenWindowEnd</c>,
/// adjusted to avoid IB order rejections at exact market boundaries.
/// </returns>
private (TimeOnly MarketOnOpenWindowStart, TimeOnly MarketOnOpenWindowEnd) GetMarketOnOpenAllowedWindow(MarketHoursSegment marketHours)
{
return (TimeOnly.FromTimeSpan(marketHours.End), TimeOnly.FromTimeSpan(marketHours.Start.Add(-TimeSpan.FromMinutes(2))));
}
}
}
@@ -0,0 +1,99 @@
/*
* 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 System.Linq;
using QuantConnect.Orders;
using QuantConnect.Securities;
using System.Collections.Generic;
using QuantConnect.Orders.TimeInForces;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides properties specific to interactive brokers
/// </summary>
public class InteractiveBrokersFixModel : InteractiveBrokersBrokerageModel
{
/// <summary>
/// Supported time in force
/// </summary>
protected override Type[] SupportedTimeInForces { get; } =
{
typeof(GoodTilCanceledTimeInForce),
typeof(DayTimeInForce),
};
/// <summary>
/// Supported order types
/// </summary>
protected override HashSet<OrderType> SupportedOrderTypes { get; } = new HashSet<OrderType>
{
OrderType.Market,
OrderType.MarketOnOpen,
OrderType.MarketOnClose,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.TrailingStop,
OrderType.ComboMarket,
OrderType.ComboLimit
};
private readonly GroupOrderCacheManager _groupOrderCacheManager = new();
/// <summary>
/// Initializes a new instance of the <see cref="InteractiveBrokersFixModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="AccountType.Margin"/></param>
public InteractiveBrokersFixModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <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 being ordered</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)
{
// only check supported combo order types
if (order is ComboOrder && order.GroupOrderManager != null && SupportedOrderTypes.Contains(order.Type))
{
if (_groupOrderCacheManager.TryGetGroupCachedOrders(order, out var orders))
{
// reject combos that mix FutureOption and Future legs
if (orders.Any(o => o.SecurityType == SecurityType.FutureOption) &&
orders.Any(o => o.SecurityType == SecurityType.Future))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.InteractiveBrokersFixModel.UnsupportedFopFutureComboOrders(this, order));
return false;
}
}
}
return base.CanSubmitOrder(security, order, out message);
}
}
}
+201
View File
@@ -0,0 +1,201 @@
/*
* 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.Collections.Generic;
using System.Linq;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Kraken Brokerage model
/// </summary>
public class KrakenBrokerageModel : DefaultBrokerageModel
{
private readonly List<string> _fiatsAvailableMargin = new() {"USD", "EUR"};
private readonly List<string> _onlyFiatsAvailableMargin = new() {"BTC", "USDT", "USDC"};
private readonly List<string> _ethAvailableMargin = new() {"REP", "XTZ", "ADA", "EOS", "TRX", "LINK" };
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Limit,
OrderType.Market,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.LimitIfTouched
};
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
/// <summary>
/// Leverage map of different coins
/// </summary>
public IReadOnlyDictionary<string, decimal> CoinLeverage { get; } = new Dictionary<string, decimal>
{
{"BTC", 5}, // only with fiats
{"ETH", 5},
{"USDT", 2}, // only with fiats
{"XMR", 2},
{"REP", 2}, // eth available
{"XRP", 3},
{"BCH", 2},
{"XTZ", 2}, // eth available
{"LTC", 3},
{"ADA", 3}, // eth available
{"EOS", 3}, // eth available
{"DASH", 3},
{"TRX", 3}, // eth available
{"LINK", 3}, // eth available
{"USDC", 3}, // only with fiats
};
/// <summary>
/// Constructor for Kraken brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public KrakenBrokerageModel(AccountType accountType = AccountType.Cash) : base(accountType)
{
}
/// <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)
{
if (!IsValidOrderSize(security, order.Quantity, out message))
{
return false;
}
message = null;
if (security.Type != SecurityType.Crypto)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Kraken does not support update of orders
/// </summary>
/// <param name="security">Security</param>
/// <param name="order">Order that should be updated</param>
/// <param name="request">Update request</param>
/// <param name="message">Outgoing message</param>
/// <returns>Always false as Kraken does not support update of orders</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, 0, Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
return false;
}
/// <summary>
/// Provides Kraken fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>Kraken fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new KrakenFeeModel();
}
/// <summary>
/// Kraken global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash)
{
return 1m;
}
// first check whether this security support margin only with fiats.
foreach (var coin in _onlyFiatsAvailableMargin.Where(coin => security.Symbol.ID.Symbol.StartsWith(coin)).Where(coin => _fiatsAvailableMargin.Any(rightFiat => security.Symbol.Value.EndsWith(rightFiat))))
{
return CoinLeverage[coin];
}
List<string> extendedCoinArray = new() {"BTC", "ETH"};
extendedCoinArray.AddRange(_fiatsAvailableMargin);
// Then check whether this security support margin with ETH.
foreach (var coin in _ethAvailableMargin.Where(coin => security.Symbol.ID.Symbol.StartsWith(coin)).Where(coin => extendedCoinArray.Any(rightFiat => security.Symbol.Value.EndsWith(rightFiat))))
{
return CoinLeverage[coin];
}
extendedCoinArray.Remove("ETH");
// At the end check all others.
foreach (var coin in CoinLeverage.Keys.Where(coin => security.Symbol.ID.Symbol.StartsWith(coin)).Where(coin => extendedCoinArray.Any(rightFiat => security.Symbol.Value.EndsWith(rightFiat))))
{
return CoinLeverage[coin];
}
return 1m;
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSD", SecurityType.Crypto, Market.Kraken);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Get default markets and specify Kraken as crypto market
/// </summary>
/// <returns>default markets</returns>
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets()
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Crypto] = Market.Kraken;
return map.ToReadOnlyDictionary();
}
}
}
@@ -0,0 +1,47 @@
/*
* 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.Interfaces;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Event arguments class for the <see cref="IBrokerage.NewBrokerageOrderNotification"/> event
/// </summary>
public class NewBrokerageOrderNotificationEventArgs
{
/// <summary>
/// The new brokerage side generated order
/// </summary>
public Order Order { get; set; }
/// <summary>
/// Creates a new instance
/// </summary>
public NewBrokerageOrderNotificationEventArgs(Order order)
{
Order = order;
}
/// <summary>
/// Returns a string describing the new brokerage order notification.
/// </summary>
override public string ToString()
{
return Order.ToString();
}
}
}
+149
View File
@@ -0,0 +1,149 @@
/*
* 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 System.Collections.Generic;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Slippage;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Oanda Brokerage Model Implementation for Back Testing.
/// </summary>
public class OandaBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// The default markets for the fxcm brokerage
/// </summary>
public new static readonly IReadOnlyDictionary<SecurityType, string> DefaultMarketMap = new Dictionary<SecurityType, string>
{
{SecurityType.Base, Market.USA},
{SecurityType.Equity, Market.USA},
{SecurityType.Option, Market.USA},
{SecurityType.Forex, Market.Oanda},
{SecurityType.Cfd, Market.Oanda}
}.ToReadOnlyDictionary();
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Limit,
OrderType.Market,
OrderType.StopMarket,
OrderType.StopLimit
};
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets => DefaultMarketMap;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="AccountType.Margin"/></param>
public OandaBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
if (accountType == AccountType.Cash)
{
throw new InvalidOperationException($"Oanda brokerage can only be used with a {AccountType.Margin} account type");
}
}
/// <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"></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 = null;
// validate security type
if (security.Type != SecurityType.Forex && security.Type != SecurityType.Cfd)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
// validate order type
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
// validate time in force
if (order.TimeInForce != TimeInForce.GoodTilCanceled)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedTimeInForce(this, order));
return false;
}
return true;
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new ConstantFeeModel(0m);
}
/// <summary>
/// Gets a new settlement model for the security
/// </summary>
/// <param name="security">The security to get a settlement model for</param>
/// <returns>The settlement model for this brokerage</returns>
public override ISettlementModel GetSettlementModel(Security security)
{
return security.Type == SecurityType.Cfd
? new AccountCurrencyImmediateSettlementModel() :
(ISettlementModel)new ImmediateSettlementModel();
}
}
}
@@ -0,0 +1,78 @@
/*
* 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.Interfaces;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Event arguments class for the <see cref="IBrokerage.OptionNotification"/> event
/// </summary>
public sealed class OptionNotificationEventArgs : EventArgs
{
/// <summary>
/// Gets the option symbol which has received a notification
/// </summary>
public Symbol Symbol { get; }
/// <summary>
/// Gets the new option position (positive for long, zero for flat, negative for short)
/// </summary>
public decimal Position { get; }
/// <summary>
/// The tag that will be used in the order
/// </summary>
public string Tag { get; }
/// <summary>
/// Initializes a new instance of the <see cref="OptionNotificationEventArgs"/> class
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="position">The new option position</param>
public OptionNotificationEventArgs(Symbol symbol, decimal position)
{
Symbol = symbol;
Position = position;
}
/// <summary>
/// Initializes a new instance of the <see cref="OptionNotificationEventArgs"/> class
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="position">The new option position</param>
/// <param name="tag">The tag to be used for the order</param>
public OptionNotificationEventArgs(Symbol symbol, decimal position, string tag)
: this(symbol, position)
{
Tag = tag;
}
/// <summary>
/// Returns the string representation of this event
/// </summary>
public override string ToString()
{
var str = $"{Symbol} position: {Position}";
if (!string.IsNullOrEmpty(Tag))
{
str += $", tag: {Tag}";
}
return str;
}
}
}
+140
View File
@@ -0,0 +1,140 @@
/*
* 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.Orders.Fees;
using QuantConnect.Securities;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Represents a brokerage model specific to Public.com.
/// </summary>
public class PublicBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// The security types supported by Public.com.
/// </summary>
private readonly HashSet<SecurityType> _supportSecurityTypes = new(
new[]
{
SecurityType.Equity,
SecurityType.Option,
SecurityType.IndexOption,
SecurityType.Crypto
});
/// <summary>
/// The order types supported by the <see cref="CanSubmitOrder"/> operation in Public.com.
/// Multi-leg combos are limit only.
/// </summary>
private readonly HashSet<OrderType> _supportOrderTypes = new(
new[]
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.ComboLimit
});
/// <summary>
/// Constructor for Public.com brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public PublicBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <summary>
/// Provides the Public.com fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>Public.com fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new PublicFeeModel();
}
/// <summary>
/// Returns true if the brokerage could accept this order. This takes into account order type, security type.
/// </summary>
/// <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;
}
// 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);
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request.
/// Public.com has no multi-leg replace endpoint, so combo orders cannot be updated.
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
if (order.GroupOrderManager != null)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
"Public.com does not support updating combo (multi-leg) orders.");
return false;
}
message = null;
return true;
}
}
}
+109
View File
@@ -0,0 +1,109 @@
/*
* 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>
/// RBI Brokerage model
/// </summary>
public class RBIBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Array's RBI supports security types
/// </summary>
private readonly HashSet<SecurityType> _supportSecurityTypes = new (new [] { SecurityType.Equity });
/// <summary>
/// Array's RBI supports order types
/// </summary>
private readonly HashSet<OrderType> _supportOrderTypes = new(new [] { OrderType.Market, OrderType.Limit, OrderType.StopMarket, OrderType.StopLimit });
/// <summary>
/// Constructor for RBI brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public RBIBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <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)
{
if (!IsValidOrderSize(security, order.Quantity, out message))
{
return false;
}
message = null;
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.RBIBrokerageModel.UnsupportedOrderType(order));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// RBI supports UpdateOrder
/// </summary>
/// <param name="security">Security</param>
/// <param name="order">Order that should be updated</param>
/// <param name="request">Update request</param>
/// <param name="message">Outgoing message</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
/// <summary>
/// Provides RBI fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>RBI fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new RBIFeeModel();
}
}
}
+208
View File
@@ -0,0 +1,208 @@
/*
* 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.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Securities;
using QuantConnect.Util;
using System;
using System.Collections.Generic;
using System.Linq;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Brokerage Model implementation for Samco
/// </summary>
public class SamcoBrokerageModel : DefaultBrokerageModel
{
private readonly HashSet<Type> _supportedTimeInForces = new()
{
typeof(GoodTilCanceledTimeInForce),
typeof(DayTimeInForce),
typeof(GoodTilDateTimeInForce)
};
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket
};
private const decimal _maxLeverage = 5m;
/// <summary>
/// Initializes a new instance of the <see cref="SamcoBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to <see cref="AccountType.Margin"/></param>
public SamcoBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <summary>
/// Returns true if the brokerage would be able to execute this order at this time assuming
/// market prices are sufficient for the fill to take place. This is used to emulate the
/// brokerage fills in backtesting and paper trading. For example some brokerages may not
/// perform executions during extended market hours. This is not intended to be checking
/// whether or not the exchange is open, that is handled in the Security.Exchange property.
/// </summary>
/// <param name="security"></param>
/// <param name="order">The order to test for execution</param>
/// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
public override bool CanExecuteOrder(Security security, Order order)
{
// validate security type
if (security.Type != SecurityType.Equity &&
security.Type != SecurityType.Option &&
security.Type != SecurityType.Future)
{
return false;
}
// validate time in force
if (!_supportedTimeInForces.Contains(order.TimeInForce.GetType()))
{
return false;
}
return true;
}
/// <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 being ordered</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 = null;
// validate security type
if (security.Type != SecurityType.Equity &&
security.Type != SecurityType.Option &&
security.Type != SecurityType.Future)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
// validate time in force
if (!_supportedTimeInForces.Contains(order.TimeInForce.GetType()))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedTimeInForce(this, order));
return false;
}
// validate order type
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">
/// If this function returns false, a brokerage message detailing why the order may not be updated
/// </param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
/// <summary>
/// Samco global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
if (security.Type == SecurityType.Equity || security.Type == SecurityType.Future || security.Type == SecurityType.Option || security.Type == SecurityType.Index)
{
return _maxLeverage;
}
throw new ArgumentException(Messages.DefaultBrokerageModel.InvalidSecurityTypeForLeverage(security), nameof(security));
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("NIFTYBEES", SecurityType.Equity, Market.India);
return SecurityBenchmark.CreateInstance(securities, symbol);
}
/// <summary>
/// Provides Samco fee model
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override IFeeModel GetFeeModel(Security security)
{
return new SamcoFeeModel();
}
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets()
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Equity] = Market.India;
map[SecurityType.Future] = Market.India;
map[SecurityType.Option] = Market.India;
return map.ToReadOnlyDictionary();
}
}
}
@@ -0,0 +1,111 @@
/*
* 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>
/// TDAmeritrade
/// </summary>
public class TDAmeritradeBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Array's TD Ameritrade supports security types
/// </summary>
private readonly HashSet<SecurityType> _supportSecurityTypes = new (new [] { SecurityType.Equity });
/// <summary>
/// Array's TD Ameritrade supports order types
/// </summary>
private readonly HashSet<OrderType> _supportOrderTypes = new(new [] { OrderType.Market, OrderType.Limit, OrderType.StopMarket, OrderType.StopLimit });
/// <summary>
/// Constructor for TDAmeritrade brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public TDAmeritradeBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <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)
{
if (!IsValidOrderSize(security, order.Quantity, out message))
{
return false;
}
message = null;
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);
}
/// <summary>
/// TDAmeritrade support Update Order
/// </summary>
/// <param name="security">Security</param>
/// <param name="order">Order that should be updated</param>
/// <param name="request">Update request</param>
/// <param name="message">Outgoing message</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
/// <summary>
/// Provides TDAmeritrade fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>TDAmeritrade fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new TDAmeritradeFeeModel();
}
}
}
@@ -0,0 +1,115 @@
/*
* 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.Orders.Fees;
using QuantConnect.Securities;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Represents a brokerage model specific to Tastytrade.
/// </summary>
public class TastytradeBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// HashSet containing the security types supported by Tastytrade.
/// </summary>
private readonly HashSet<SecurityType> _supportSecurityTypes = new(
new[]
{
SecurityType.Equity,
SecurityType.Option,
SecurityType.IndexOption,
SecurityType.Future,
SecurityType.FutureOption
});
/// <summary>
/// HashSet containing the order types supported by the <see cref="CanSubmitOrder"/> operation in Tastytrade.
/// </summary>
private readonly HashSet<OrderType> _supportOrderTypes = new(
new[]
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.ComboLimit
});
/// <summary>
/// The set of <see cref="OrderType"/> values that cannot be used for cross-zero execution.
/// </summary>
private static readonly IReadOnlySet<OrderType> NotSupportedCrossZeroOrderTypes = new HashSet<OrderType>()
{
OrderType.ComboLimit
};
/// <summary>
/// Constructor for Tastytrade brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public TastytradeBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <summary>
/// Provides Tastytrade fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>TradeStation fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new TastytradeFeeModel();
}
/// <summary>
/// Returns true if the brokerage could accept this order. This takes into account order type, security type.
/// </summary>
/// <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;
}
if (!BrokerageExtensions.ValidateCrossZeroOrder(this, security, order, out message, NotSupportedCrossZeroOrderTypes))
{
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
}
}
@@ -0,0 +1,86 @@
/*
* 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.Collections.Generic;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides Bloomberg EMSX (TerminalLink) specific properties.
/// </summary>
public class TerminalLinkBrokerageModel : DefaultBrokerageModel
{
private readonly HashSet<SecurityType> _supportedSecurityTypes = new()
{
SecurityType.Equity,
SecurityType.Option,
SecurityType.Future,
};
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Market,
OrderType.MarketOnOpen,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
};
/// <summary>
/// Initializes a new instance of the <see cref="TerminalLinkBrokerageModel"/> class.
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public TerminalLinkBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <summary>
/// Returns true if the brokerage could accept this order.
/// </summary>
/// <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>
public override bool CanSubmitOrder(Security security, Order order, out BrokerageMessageEvent message)
{
if (!_supportedSecurityTypes.Contains(security.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// TerminalLink does not allow modifying live orders; the EMSX brokerage rejects updates.
/// </summary>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
return false;
}
}
}
@@ -0,0 +1,205 @@
/*
* 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.Orders;
using QuantConnect.Securities;
using QuantConnect.Orders.Fees;
using System.Collections.Generic;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Represents a brokerage model specific to TradeStation.
/// </summary>
public class TradeStationBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// The default start time of the <see cref="OrderType.MarketOnOpen"/> order submission window.
/// Example: 6:00 (6:00 AM).
/// </summary>
private static readonly TimeOnly _mooWindowStart = new(6, 0, 0);
/// <summary>
/// HashSet containing the security types supported by TradeStation.
/// </summary>
private readonly HashSet<SecurityType> _supportSecurityTypes = new(
new[]
{
SecurityType.Equity,
SecurityType.Option,
SecurityType.Future,
SecurityType.IndexOption
});
/// <summary>
/// Defines the default set of <see cref="SecurityType"/> values that support <see cref="OrderType.MarketOnOpen"/> orders.
/// </summary>
private static readonly IReadOnlySet<SecurityType> _defaultMarketOnOpenSupportedSecurityTypes = new HashSet<SecurityType>
{
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,
OrderType.StopLimit,
OrderType.ComboMarket,
OrderType.ComboLimit,
OrderType.MarketOnOpen,
OrderType.MarketOnClose,
OrderType.TrailingStop
});
/// <summary>
/// The set of <see cref="OrderType"/> values that cannot be used for cross-zero execution.
/// </summary>
private static readonly IReadOnlySet<OrderType> NotSupportedCrossZeroOrderTypes = new HashSet<OrderType>()
{
OrderType.ComboMarket,
OrderType.ComboLimit,
OrderType.MarketOnOpen,
OrderType.MarketOnClose
};
/// <summary>
/// Constructor for TradeStation brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public TradeStationBrokerageModel(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 TradeStationFeeModel();
}
/// <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;
var supportsOutsideTradingHours = (order.Properties as TradeStationOrderProperties)?.OutsideRegularTradingHours ?? false;
if (supportsOutsideTradingHours && (order.Type != OrderType.Limit || order.SecurityType != SecurityType.Equity))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupportedOutsideRegularMarketHours",
"To place an order outside regular trading hours, please use a limit order and ensure the security is an equity.");
return false;
}
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;
}
if (!BrokerageExtensions.ValidateCrossZeroOrder(this, security, order, out message, NotSupportedCrossZeroOrderTypes))
{
return false;
}
if (!BrokerageExtensions.ValidateMarketOnOpenOrder(security, order, GetMarketOnOpenAllowedWindow, _defaultMarketOnOpenSupportedSecurityTypes, out message))
{
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// TradeStation support Update Order
/// </summary>
/// <param name="security">Security</param>
/// <param name="order">Order that should be updated</param>
/// <param name="request">Update request</param>
/// <param name="message">Outgoing message</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
if (BrokerageExtensions.OrderCrossesZero(security.Holdings.Quantity, order.Quantity)
&& request.Quantity != null && request.Quantity != order.Quantity)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "UpdateRejected",
Messages.DefaultBrokerageModel.UnsupportedCrossZeroOrderUpdate(this));
return false;
}
if (IsComboOrderType(order.Type) && request.Quantity != null && request.Quantity != order.Quantity)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported", Messages.DefaultBrokerageModel.UnsupportedUpdateQuantityOrder(this, order.Type));
return false;
}
return true;
}
/// <summary>
/// Determines if the provided order type is a combo order.
/// </summary>
/// <param name="orderType">The order type to check.</param>
/// <returns>True if the order type is a combo order; otherwise, false.</returns>
private static bool IsComboOrderType(OrderType orderType)
{
return orderType == OrderType.ComboMarket || orderType == OrderType.ComboLimit;
}
/// <summary>
/// Returns the TradeStation Market-on-Open submission window (6:00 AM start, slightly before market open end).
/// </summary>
/// <param name="marketHours">The market hours segment for the security.</param>
/// <returns>A tuple with <c>MarketOnOpenWindowStart</c> and <c>MarketOnOpenWindowEnd</c>.</returns>
private (TimeOnly MarketOnOpenWindowStart, TimeOnly MarketOnOpenWindowEnd) GetMarketOnOpenAllowedWindow(MarketHoursSegment marketHours)
{
return (_mooWindowStart, TimeOnly.FromTimeSpan(marketHours.Start.Add(-TimeSpan.FromMinutes(1))));
}
}
}
+239
View File
@@ -0,0 +1,239 @@
/*
* 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 System.Collections.Generic;
using QuantConnect.Data.Market;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides tradier specific properties
/// </summary>
public class TradierBrokerageModel : DefaultBrokerageModel
{
private static readonly MarketHoursSegment PreMarketSession = new MarketHoursSegment(
MarketHoursState.PreMarket,
new TimeSpan(4, 0, 0),
new TimeSpan(9, 24, 0));
private static readonly MarketHoursSegment PostMarketSession = new MarketHoursSegment(
MarketHoursState.PostMarket,
new TimeSpan(16, 0, 0),
new TimeSpan(19, 55, 0));
private readonly HashSet<OrderType> _supportedOrderTypes = new HashSet<OrderType>
{
OrderType.Limit,
OrderType.Market,
OrderType.StopMarket,
OrderType.StopLimit
};
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to
/// <see cref="QuantConnect.AccountType.Margin"/></param>
public TradierBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <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 = null;
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
var securityType = order.SecurityType;
if (securityType != SecurityType.Equity && securityType != SecurityType.Option && securityType != SecurityType.IndexOption)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.TradierBrokerageModel.UnsupportedSecurityType);
return false;
}
if (order.TimeInForce is not GoodTilCanceledTimeInForce && order.TimeInForce is not DayTimeInForce)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.TradierBrokerageModel.UnsupportedTimeInForceType);
return false;
}
if (security.Holdings.Quantity + order.Quantity < 0)
{
if (order.TimeInForce is GoodTilCanceledTimeInForce)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "ShortOrderIsGtc", Messages.TradierBrokerageModel.ShortOrderIsGtc);
return false;
}
else if (security.Price < 5)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "SellShortOrderLastPriceBelow5", Messages.TradierBrokerageModel.SellShortOrderLastPriceBelow5);
return false;
}
}
if (order.AbsoluteQuantity < 1 || order.AbsoluteQuantity > 10000000)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "IncorrectOrderQuantity", Messages.TradierBrokerageModel.IncorrectOrderQuantity);
return false;
}
if (!CanExecuteOrderImpl(security, order, out var canSubmit))
{
if (!canSubmit)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "ExtendedMarket",
Messages.TradierBrokerageModel.ExtendedMarketHoursTradingNotSupportedOutsideExtendedSession(PreMarketSession, PostMarketSession));
return false;
}
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "ExtendedMarket",
Messages.TradierBrokerageModel.ExtendedMarketHoursTradingNotSupported);
}
if (!BrokerageExtensions.ValidateCrossZeroOrder(this, security, order, out message))
{
return false;
}
// tradier order limits
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
// Tradier doesn't allow updating order quantities
if (request.Quantity != null && request.Quantity != order.Quantity)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "UpdateRejected",
Messages.TradierBrokerageModel.OrderQuantityUpdateNotSupported);
return false;
}
return true;
}
private static bool CanExecuteOrderImpl(Security security, Order order, out bool canSubmit)
{
if (!security.Exchange.ExchangeOpen)
{
var tradeOnExtendedHours = (order.Properties as TradierOrderProperties)?.OutsideRegularTradingHours ?? false;
if (!tradeOnExtendedHours ||
order.Type != OrderType.Limit ||
order.Symbol.SecurityType != SecurityType.Equity ||
!IsWithinTradierExtendedSession(security.LocalTime))
{
// if OutsideRegularTradingHours is false, allow order submission since it will be processed on market open
canSubmit = !tradeOnExtendedHours;
return false;
}
}
canSubmit = true;
return true;
}
/// <summary>
/// Returns true if the brokerage would be able to execute this order at this time assuming
/// market prices are sufficient for the fill to take place. This is used to emulate the
/// brokerage fills in backtesting and paper trading. For example some brokerages may not perform
/// executions during extended market hours. This is not intended to be checking whether or not
/// the exchange is open, that is handled in the Security.Exchange property.
/// </summary>
/// <param name="security">The security being ordered</param>
/// <param name="order">The order to test for execution</param>
/// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
public override bool CanExecuteOrder(Security security, Order order)
{
return CanExecuteOrderImpl(security, order, out _);
}
/// <summary>
/// Applies the split to the specified order ticket
/// </summary>
/// <param name="tickets">The open tickets matching the split event</param>
/// <param name="split">The split event data</param>
public override void ApplySplit(List<OrderTicket> tickets, Split split)
{
// tradier cancels reverse splits
var splitFactor = split.SplitFactor;
if (splitFactor > 1.0m)
{
tickets.ForEach(ticket => ticket.Cancel(Messages.TradierBrokerageModel.OpenOrdersCancelOnReverseSplitSymbols));
}
else
{
base.ApplySplit(tickets, split);
}
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
// Trading stocks at Tradier Brokerage is free
return new ConstantFeeModel(0m);
}
private static bool IsWithinTradierExtendedSession(DateTime localTime)
{
return PreMarketSession.Contains(localTime.TimeOfDay) || PostMarketSession.Contains(localTime.TimeOfDay);
}
}
}
@@ -0,0 +1,227 @@
/*
* 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 System.Collections.Generic;
using System.Linq;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Provides properties specific to Trading Technologies
/// </summary>
public class TradingTechnologiesBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// The default markets for Trading Technologies
/// </summary>
public new static readonly IReadOnlyDictionary<SecurityType, string> DefaultMarketMap = new Dictionary<SecurityType, string>
{
{SecurityType.Future, Market.CME}
}.ToReadOnlyDictionary();
private readonly Type[] _supportedTimeInForces =
{
typeof(GoodTilCanceledTimeInForce),
typeof(DayTimeInForce)
};
private readonly HashSet<OrderType> _supportedOrderTypes = new()
{
OrderType.Limit,
OrderType.Market,
OrderType.StopMarket,
OrderType.StopLimit
};
/// <summary>
/// Initializes a new instance of the <see cref="TradingTechnologiesBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="AccountType.Margin"/></param>
public TradingTechnologiesBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets => DefaultMarketMap;
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
// Equivalent to no benchmark
return new FuncBenchmark(x => 0);
}
/// <summary>
/// Gets a new fee model that represents this brokerage's fee structure
/// </summary>
/// <param name="security">The security to get a fee model for</param>
/// <returns>The new fee model for this brokerage</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new ConstantFeeModel(0);
}
/// <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 being ordered</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 = null;
// validate security type
if (security.Type != SecurityType.Future)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
// validate order type
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
// validate time in force
if (!_supportedTimeInForces.Contains(order.TimeInForce.GetType()))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedTimeInForce(this, order));
return false;
}
// validate stop orders prices
var stopMarket = order as StopMarketOrder;
if (stopMarket != null)
{
return IsValidOrderPrices(security, OrderType.StopMarket, stopMarket.Direction, stopMarket.StopPrice, security.Price, ref message);
}
var stopLimit = order as StopLimitOrder;
if (stopLimit != null)
{
return IsValidOrderPrices(security, OrderType.StopLimit, stopLimit.Direction, stopLimit.StopPrice, stopLimit.LimitPrice, ref message);
}
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
/// <summary>
/// Returns true if the brokerage would be able to execute this order at this time assuming
/// market prices are sufficient for the fill to take place. This is used to emulate the
/// brokerage fills in backtesting and paper trading. For example some brokerages may not perform
/// executions during extended market hours. This is not intended to be checking whether or not
/// the exchange is open, that is handled in the Security.Exchange property.
/// </summary>
/// <param name="security"></param>
/// <param name="order">The order to test for execution</param>
/// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
public override bool CanExecuteOrder(Security security, Order order)
{
return order.SecurityType == SecurityType.Future;
}
/// <summary>
/// Validates stopmarket/stoplimit order prices, pass security.Price for limit/stop if n/a
/// </summary>
private static bool IsValidOrderPrices(
Security security,
OrderType orderType,
OrderDirection orderDirection,
decimal stopPrice,
decimal limitPrice,
ref BrokerageMessageEvent message
)
{
// validate stop market order prices
if (orderType == OrderType.StopMarket &&
(orderDirection == OrderDirection.Buy && stopPrice <= security.Price ||
orderDirection == OrderDirection.Sell && stopPrice >= security.Price))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.TradingTechnologiesBrokerageModel.InvalidStopMarketOrderPrice);
return false;
}
// validate stop limit order prices
if (orderType == OrderType.StopLimit)
{
if (orderDirection == OrderDirection.Buy && stopPrice <= security.Price ||
orderDirection == OrderDirection.Sell && stopPrice >= security.Price)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.TradingTechnologiesBrokerageModel.InvalidStopLimitOrderPrice);
return false;
}
if (orderDirection == OrderDirection.Buy && limitPrice < stopPrice ||
orderDirection == OrderDirection.Sell && limitPrice > stopPrice)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.TradingTechnologiesBrokerageModel.InvalidStopLimitOrderLimitPrice);
return false;
}
}
return true;
}
}
}
+158
View File
@@ -0,0 +1,158 @@
/*
* 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.Collections.Generic;
using QuantConnect.Logging;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Securities;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Represents a brokerage model specific to Webull.
/// </summary>
public class WebullBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Flag to track if we've already logged a message about market orders only supporting Day TIF. We only want to log this once to avoid spamming the logs.
/// </summary>
private bool _marketOrderDayTimeInForceLogged;
/// <summary>
/// Maps each supported security type to the order types Webull allows for it.
/// </summary>
private static readonly Dictionary<SecurityType, HashSet<OrderType>> _supportedOrderTypesBySecurityType =
new Dictionary<SecurityType, HashSet<OrderType>>
{
{
SecurityType.Equity, new HashSet<OrderType>
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit,
OrderType.TrailingStop
}
},
{
SecurityType.Option, new HashSet<OrderType>
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit
}
},
{
SecurityType.IndexOption, new HashSet<OrderType>
{
OrderType.Market,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit
}
}
};
/// <summary>
/// Constructor for Webull brokerage model.
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public WebullBrokerageModel(AccountType accountType = AccountType.Margin)
: base(accountType)
{
}
/// <summary>
/// Provides the Webull fee model.
/// </summary>
/// <param name="security">Security</param>
/// <returns>Webull fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new WebullFeeModel();
}
/// <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 (!_supportedOrderTypesBySecurityType.TryGetValue(security.Type, out var supportedOrderTypes))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
if (!supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, supportedOrderTypes));
return false;
}
if (!_marketOrderDayTimeInForceLogged && order.Type == OrderType.Market && order.TimeInForce is not DayTimeInForce)
{
_marketOrderDayTimeInForceLogged = true;
Log.Trace("WebullBrokerageModel.CanSubmitOrder: Market orders support only Day TIF, which is set automatically by the brokerage.");
}
// Options and IndexOptions have per-direction TimeInForce restrictions.
// https://developer.webull.com/apis/docs/trade-api/options#time-in-force
// - Sell orders: Day only
if (security.Type == SecurityType.Option || security.Type == SecurityType.IndexOption)
{
if (order.Direction == OrderDirection.Sell && order.TimeInForce is not DayTimeInForce)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.WebullBrokerageModel.InvalidTimeInForceForOptionSellOrder(order));
return false;
}
}
if (order.Properties is WebullOrderProperties { OutsideRegularTradingHours: true })
{
if (security.Type is not SecurityType.Equity)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.WebullBrokerageModel.OutsideRegularTradingHoursNotSupportedForSecurityType(security));
return false;
}
if (order.Type == OrderType.Market)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.WebullBrokerageModel.MarketOrdersNotSupportedOutsideRegularTradingHours());
return false;
}
}
return base.CanSubmitOrder(security, order, out message);
}
}
}
@@ -0,0 +1,106 @@
/*
* 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>
/// Wolverine Brokerage model
/// </summary>
public class WolverineBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Supported order types
/// </summary>
private HashSet<OrderType> SupportedOrderTypes { get; } =
[
OrderType.Market,
OrderType.MarketOnClose,
OrderType.Limit,
OrderType.StopMarket,
OrderType.StopLimit
];
/// <summary>
/// Constructor for Wolverine brokerage model
/// </summary>
/// <param name="accountType">Cash or Margin</param>
public WolverineBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <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)
{
if (!IsValidOrderSize(security, order.Quantity, out message))
{
return false;
}
if (security.Type != SecurityType.Equity && security.Type != SecurityType.Option)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
if (!SupportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, SupportedOrderTypes));
return false;
}
return base.CanSubmitOrder(security, order, out message);
}
/// <summary>
/// Wolverine does not support update of orders
/// </summary>
/// <param name="security">Security</param>
/// <param name="order">Order that should be updated</param>
/// <param name="request">Update request</param>
/// <param name="message">Outgoing message</param>
/// <returns>Always false as Wolverine does not support update of orders</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, 0, Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
return false;
}
/// <summary>
/// Provides Wolverine fee model
/// </summary>
/// <param name="security">Security</param>
/// <returns>Wolverine fee model</returns>
public override IFeeModel GetFeeModel(Security security)
{
return new WolverineFeeModel();
}
}
}
+192
View File
@@ -0,0 +1,192 @@
/*
* 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 System.Collections.Generic;
using System.Linq;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.TimeInForces;
using QuantConnect.Securities;
using QuantConnect.Util;
namespace QuantConnect.Brokerages
{
/// <summary>
/// Brokerage Model implementation for Zerodha
/// </summary>
public class ZerodhaBrokerageModel : DefaultBrokerageModel
{
private readonly Type[] _supportedTimeInForces =
{
typeof(GoodTilCanceledTimeInForce),
typeof(DayTimeInForce),
typeof(GoodTilDateTimeInForce)
};
private readonly HashSet<OrderType> _supportedOrderTypes = new HashSet<OrderType>
{
OrderType.Limit,
OrderType.Market,
OrderType.StopMarket,
OrderType.StopLimit
};
private const decimal _maxLeverage = 5m;
/// <summary>
/// Initializes a new instance of the <see cref="ZerodhaBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modelled, defaults to
/// <see cref="AccountType.Margin"/></param>
public ZerodhaBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
}
/// <summary>
/// Returns true if the brokerage would be able to execute this order at this time assuming
/// market prices are sufficient for the fill to take place. This is used to emulate the
/// brokerage fills in backtesting and paper trading. For example some brokerages may not perform
/// executions during extended market hours. This is not intended to be checking whether or not
/// the exchange is open, that is handled in the Security.Exchange property.
/// </summary>
/// <param name="security"></param>
/// <param name="order">The order to test for execution</param>
/// <returns>True if the brokerage would be able to perform the execution, false otherwise</returns>
public override bool CanExecuteOrder(Security security, Order order)
{
// validate security type
if (security.Type != SecurityType.Equity)
{
return false;
}
// validate time in force
if (!_supportedTimeInForces.Contains(order.TimeInForce.GetType()))
{
return false;
}
return true;
}
/// <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 being ordered</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 = null;
// validate security type
if (security.Type != SecurityType.Equity)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
// validate order type
if (!_supportedOrderTypes.Contains(order.Type))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order, _supportedOrderTypes));
return false;
}
// validate time in force
if (!_supportedTimeInForces.Contains(order.TimeInForce.GetType()))
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedTimeInForce(this, order));
return false;
}
return true;
}
/// <summary>
/// Returns true if the brokerage would allow updating the order as specified by the request
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage would allow updating the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request, out BrokerageMessageEvent message)
{
message = null;
return true;
}
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets();
/// <summary>
/// Zerodha global leverage rule
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override decimal GetLeverage(Security security)
{
if (AccountType == AccountType.Cash || security.IsInternalFeed() || security.Type == SecurityType.Base)
{
return 1m;
}
if (security.Type == SecurityType.Equity || security.Type == SecurityType.Future || security.Type == SecurityType.Option || security.Type == SecurityType.Index)
{
return _maxLeverage;
}
throw new ArgumentException(Messages.DefaultBrokerageModel.InvalidSecurityTypeForLeverage(security), nameof(security));
}
/// <summary>
/// Provides Zerodha fee model
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override IFeeModel GetFeeModel(Security security)
{
return new ZerodhaFeeModel();
}
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets()
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.Equity] = Market.India;
return map.ToReadOnlyDictionary();
}
}
}
+168
View File
@@ -0,0 +1,168 @@
/*
* 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 System.Collections.Generic;
using System.Linq;
using QuantConnect.Benchmarks;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Securities;
using QuantConnect.Securities.CryptoFuture;
using QuantConnect.Util;
namespace QuantConnect.Brokerages;
public class dYdXBrokerageModel : DefaultBrokerageModel
{
/// <summary>
/// Gets a map of the default markets to be used for each security type
/// </summary>
public override IReadOnlyDictionary<SecurityType, string> DefaultMarkets { get; } = GetDefaultMarkets(Market.DYDX);
/// <summary>
/// Initializes a new instance of the <see cref="dYdXBrokerageModel"/> class
/// </summary>
/// <param name="accountType">The type of account to be modeled, defaults to <see cref="AccountType.Margin"/></param>
public dYdXBrokerageModel(AccountType accountType = AccountType.Margin) : base(accountType)
{
if (accountType != AccountType.Margin)
{
throw new ArgumentException("dYdXBrokerageModel only supports margin accounts", nameof(accountType));
}
}
/// <summary>
/// Gets a new buying power model for the security, returning the default model with the security's configured leverage.
/// For cash accounts, leverage = 1 is used.
/// </summary>
/// <param name="security">The security to get a buying power model for</param>
/// <returns>The buying power model for this brokerage/security</returns>
public override IBuyingPowerModel GetBuyingPowerModel(Security security)
{
return security?.Type switch
{
SecurityType.CryptoFuture => new SecurityMarginModel(GetLeverage(security)),
_ => base.GetBuyingPowerModel(security)
};
}
/// <summary>
/// Provides dYdX fee model
/// </summary>
/// <param name="security"></param>
/// <returns></returns>
public override IFeeModel GetFeeModel(Security security)
{
return security.Type switch
{
SecurityType.CryptoFuture => new dYdXFeeModel(),
_ => base.GetFeeModel(security)
};
}
/// <summary>
/// Gets a new margin interest rate model for the security
/// </summary>
/// <param name="security">The security to get a margin interest rate model for</param>
/// <returns>The margin interest rate model for this brokerage</returns>
public override IMarginInterestRateModel GetMarginInterestRateModel(Security security)
{
// only applies for perpetual futures
return security.Type switch
{
SecurityType.CryptoFuture => new dYdXFutureMarginInterestRateModel(),
_ => base.GetMarginInterestRateModel(security)
};
}
/// <summary>
/// Get the benchmark for this model
/// </summary>
/// <param name="securities">SecurityService to create the security with if needed</param>
/// <returns>The benchmark for this brokerage</returns>
public override IBenchmark GetBenchmark(SecurityManager securities)
{
var symbol = Symbol.Create("BTCUSD", SecurityType.CryptoFuture, Market.DYDX);
return SecurityBenchmark.CreateInstance(securities, symbol);
//todo default conversion?
}
/// <summary>
/// Returns true if the brokerage could accept this order update. This takes into account
/// order type, security type, and order size limits. dYdX can only update inverse, linear, and option orders
/// </summary>
/// <param name="security">The security of the order</param>
/// <param name="order">The order to be updated</param>
/// <param name="request">The requested update to be made to the order</param>
/// <param name="message">If this function returns false, a brokerage message detailing why the order may not be updated</param>
/// <returns>True if the brokerage could update the order, false otherwise</returns>
public override bool CanUpdateOrder(Security security, Order order, UpdateOrderRequest request,
out BrokerageMessageEvent message)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.OrderUpdateNotSupported);
return false;
}
/// <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)
{
if (security.Type != SecurityType.CryptoFuture)
{
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedSecurityType(this, security));
return false;
}
message = null;
bool quantityIsValid;
switch (order)
{
case StopLimitOrder:
case StopMarketOrder:
case LimitOrder:
case MarketOrder:
quantityIsValid = IsValidOrderSize(security, Math.Abs(order.Quantity), out message);
break;
default:
message = new BrokerageMessageEvent(BrokerageMessageType.Warning, "NotSupported",
Messages.DefaultBrokerageModel.UnsupportedOrderType(this, order,
[OrderType.StopMarket, OrderType.StopLimit, OrderType.Market, OrderType.Limit]));
return false;
}
return quantityIsValid;
}
private static IReadOnlyDictionary<SecurityType, string> GetDefaultMarkets(string marketName)
{
var map = DefaultMarketMap.ToDictionary();
map[SecurityType.CryptoFuture] = marketName;
return map.ToReadOnlyDictionary();
}
}