chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 && IApi.SendUserEmail
|
||||
/// Error -> IResultHandler.Error && 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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))));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user