chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:02:50 +08:00
commit 0fc60fdcb1
5008 changed files with 910633 additions and 0 deletions
+29
View File
@@ -0,0 +1,29 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Provides an implementation of the <see cref="OrderProperties"/> specific to Alpaca order.
/// </summary>
public class AlpacaOrderProperties : OrderProperties
{
/// <summary>
/// Flag to allow orders to also trigger or fill outside of regular trading hours.
/// </summary>
public bool OutsideRegularTradingHours { get; set; }
}
}
+41
View File
@@ -0,0 +1,41 @@
/*
* 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.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to Binance brokerage
/// </summary>
public class BinanceOrderProperties : OrderProperties
{
/// <summary>
/// This flag will ensure the order executes only as a maker (no fee) order.
/// If part of the order results in taking liquidity rather than providing,
/// it will be rejected and no part of the order will execute.
/// Note: this flag is only applied to Limit orders.
/// </summary>
public bool PostOnly { get; set; }
/// <summary>
/// Returns a new instance clone of this object
/// </summary>
public override IOrderProperties Clone()
{
return (BinanceOrderProperties)MemberwiseClone();
}
}
}
+47
View File
@@ -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.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to Bitfinex brokerage
/// </summary>
public class BitfinexOrderProperties : OrderProperties
{
/// <summary>
/// This flag will ensure the order executes only as a maker (no fee) order.
/// If part of the order results in taking liquidity rather than providing,
/// it will be rejected and no part of the order will execute.
/// Note: this flag is only applied to Limit orders.
/// </summary>
public bool PostOnly { get; set; }
/// <summary>
/// The hidden order option ensures an order does not appear in the order book; thus does not influence other market participants.
/// If you place a hidden order, you will always pay the taker fee. If you place a limit order that hits a hidden order, you will always pay the maker fee.
/// </summary>
public bool Hidden { get; set; }
/// <summary>
/// Returns a new instance clone of this object
/// </summary>
public override IOrderProperties Clone()
{
return (BitfinexOrderProperties)MemberwiseClone();
}
}
}
@@ -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 System.Collections.Generic;
namespace QuantConnect.Orders
{
/// <summary>
/// Event used when the brokerage order id has changed
/// </summary>
public class BrokerageOrderIdChangedEvent
{
/// <summary>
/// The lean order ID.
/// </summary>
public int OrderId { get; set; }
/// <summary>
/// Brokerage Id for this order
/// </summary>
public List<string> BrokerId { get; set; }
/// <summary>
/// Returns a string that represents the current <see cref="BrokerageOrderIdChangedEvent"/>.
/// </summary>
/// <returns>
/// A string containing the order ID and associated brokerage IDs.
/// </returns>
public override string ToString()
{
var brokerIds = BrokerId != null ? string.Join(", ", BrokerId) : "null";
return $"OrderId: {OrderId}, BrokerId: [{brokerIds}]";
}
}
}
+37
View File
@@ -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.Orders;
/// <summary>
/// Class containing Bybit OrderProperties
/// </summary>
public class BybitOrderProperties : OrderProperties
{
/// <summary>
/// This flag will ensure the order executes only as a maker (no fee) order.
/// If part of the order results in taking liquidity rather than providing,
/// it will be rejected and no part of the order will execute.
/// Note: this flag is only applied to Limit orders.
/// </summary>
public bool PostOnly { get; set; }
/// <summary>
/// This flag will ensure your position can only reduce in size if the order is triggered.
/// <seealso href="https://www.bybit.com/en-US/help-center/s/article/What-is-a-Reduce-Only-Order"/>
/// </summary>
public bool? ReduceOnly { get; set; }
}
+56
View File
@@ -0,0 +1,56 @@
/*
* 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.Orders
{
/// <summary>
/// Defines a request to cancel an order
/// </summary>
public class CancelOrderRequest : OrderRequest
{
/// <summary>
/// Gets <see cref="Orders.OrderRequestType.Cancel"/>
/// </summary>
public override OrderRequestType OrderRequestType
{
get { return OrderRequestType.Cancel; }
}
/// <summary>
/// Initializes a new instance of the <see cref="CancelOrderRequest"/> class
/// </summary>
/// <param name="time">The time this cancelation was requested</param>
/// <param name="orderId">The order id to be canceled</param>
/// <param name="tag">A new tag for the order</param>
public CancelOrderRequest(DateTime time, int orderId, string tag)
: base(time, orderId, tag)
{
}
/// <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.CancelOrderRequest.ToString(this);
}
}
}
@@ -0,0 +1,32 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to Charles Schwab brokerage
/// </summary>
public class CharlesSchwabOrderProperties : OrderProperties
{
/// <summary>
/// If set to true, allows orders to also trigger or fill outside of regular trading hours.
/// </summary>
/// <remarks>
/// Schwab offers extended hours trading for stocks and ETFs during the business week.
/// Stock trading does not occur on weekends, holidays, or on days the market is closed.
/// </remarks>
public bool ExtendedRegularTradingHours { get; set; }
}
}
+38
View File
@@ -0,0 +1,38 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to Coinbase brokerage
/// </summary>
public class CoinbaseOrderProperties : OrderProperties
{
/// <summary>
/// This flag will ensure the order executes only as a maker (no fee) order.
/// If part of the order results in taking liquidity rather than providing,
/// it will be rejected and no part of the order will execute.
/// Note: this flag is only applied to Limit orders.
/// </summary>
public bool PostOnly { get; set; }
/// <summary>
/// Gets or sets a value indicating whether self-trade prevention is enabled for this order.
/// Self-trade prevention helps prevent an order from crossing against the same user,
/// reducing the risk of unintentional trades within the same account.
/// </summary>
public bool SelfTradePreventionId { get; set; }
}
}
+98
View File
@@ -0,0 +1,98 @@
/*
* 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 Newtonsoft.Json;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Combo leg limit order type
/// </summary>
/// <remarks>Limit price per leg in the combo order</remarks>
public class ComboLegLimitOrder : ComboOrder
{
/// <summary>
/// Combo Limit Leg Order Type
/// </summary>
public override OrderType Type => OrderType.ComboLegLimit;
/// <summary>
/// Limit price for this order.
/// </summary>
[JsonProperty(PropertyName = "limitPrice")]
public decimal LimitPrice { get; internal set; }
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
public ComboLegLimitOrder() : base()
{
}
/// <summary>
/// New limit order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="groupOrderManager">Manager for the orders in the group</param>
/// <param name="limitPrice">Price the order should be filled at if a limit order</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public ComboLegLimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, DateTime time, GroupOrderManager groupOrderManager,
string tag = "", IOrderProperties properties = null)
: base(symbol, quantity, time, groupOrderManager, tag, properties)
{
GroupOrderManager = groupOrderManager;
LimitPrice = limitPrice;
}
/// <summary>
/// Gets the order value in units of the security's quote currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
return LimitOrder.CalculateOrderValue(Quantity, LimitPrice, security.Price);
}
/// <summary>
/// Modifies the state of this order to match the update request
/// </summary>
/// <param name="request">The request to update this order object</param>
public override void ApplyUpdateOrderRequest(UpdateOrderRequest request)
{
base.ApplyUpdateOrderRequest(request);
if (request.LimitPrice.HasValue)
{
LimitPrice = request.LimitPrice.Value;
}
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new ComboLegLimitOrder { LimitPrice = LimitPrice };
CopyTo(order);
return order;
}
}
}
+90
View File
@@ -0,0 +1,90 @@
/*
* 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;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Combo limit order type
/// </summary>
/// <remarks>Single limit price for the whole combo order</remarks>
public class ComboLimitOrder : ComboOrder
{
/// <summary>
/// Combo Limit Order Type
/// </summary>
public override OrderType Type => OrderType.ComboLimit;
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
public ComboLimitOrder() : base()
{
}
/// <summary>
/// New limit order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="groupOrderManager">Manager for the orders in the group</param>
/// <param name="limitPrice">Price the order should be filled at if a limit order</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public ComboLimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, DateTime time, GroupOrderManager groupOrderManager,
string tag = "", IOrderProperties properties = null)
: base(symbol, quantity, time, groupOrderManager, tag, properties)
{
GroupOrderManager.LimitPrice = limitPrice;
}
/// <summary>
/// Gets the order value in units of the security's quote currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
return LimitOrder.CalculateOrderValue(Quantity, GroupOrderManager.LimitPrice, security.Price);
}
/// <summary>
/// Modifies the state of this order to match the update request
/// </summary>
/// <param name="request">The request to update this order object</param>
public override void ApplyUpdateOrderRequest(UpdateOrderRequest request)
{
base.ApplyUpdateOrderRequest(request);
if (request.LimitPrice.HasValue)
{
GroupOrderManager.LimitPrice = request.LimitPrice.Value;
}
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new ComboLimitOrder();
CopyTo(order);
return order;
}
}
}
+74
View File
@@ -0,0 +1,74 @@
/*
* 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;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Combo market order type
/// </summary>
public class ComboMarketOrder : ComboOrder
{
/// <summary>
/// Combo Market Order Type
/// </summary>
public override OrderType Type => OrderType.ComboMarket;
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
public ComboMarketOrder() : base()
{
}
/// <summary>
/// New market order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="groupOrderManager">Manager for the orders in the group</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public ComboMarketOrder(Symbol symbol, decimal quantity, DateTime time, GroupOrderManager groupOrderManager, string tag = "",
IOrderProperties properties = null)
: base(symbol, quantity, time, groupOrderManager, tag, properties)
{
}
/// <summary>
/// Gets the order value in units of the security's quote currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
return Quantity * security.Price;
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new ComboMarketOrder();
CopyTo(order);
return order;
}
}
}
+91
View File
@@ -0,0 +1,91 @@
/*
* 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.Orders
{
/// <summary>
/// Combo order type
/// </summary>
public abstract class ComboOrder : Order
{
private decimal _ratio;
/// <summary>
/// Number of shares to execute.
/// For combo orders, we store the ratio of each leg instead of the quantity,
/// and the actual quantity is calculated when requested using the group order manager quantity.
/// This allows for a single quantity update to be applied to all the legs of the combo.
/// </summary>
public override decimal Quantity
{
get
{
return _ratio.GetOrderLegGroupQuantity(GroupOrderManager).Normalize();
}
internal set
{
_ratio = value.GetOrderLegRatio(GroupOrderManager);
}
}
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
public ComboOrder() : base()
{
}
/// <summary>
/// New market order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="groupOrderManager">Manager for the orders in the group</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public ComboOrder(Symbol symbol, decimal quantity, DateTime time, GroupOrderManager groupOrderManager, string tag = "",
IOrderProperties properties = null)
: base(symbol, 0m, time, tag, properties)
{
GroupOrderManager = groupOrderManager;
Quantity = quantity;
}
/// <summary>
/// Modifies the state of this order to match the update request
/// </summary>
/// <param name="request">The request to update this order object</param>
public override void ApplyUpdateOrderRequest(UpdateOrderRequest request)
{
if (request.OrderId != Id)
{
throw new ArgumentException("Attempted to apply updates to the incorrect order!");
}
if (request.Tag != null)
{
Tag = request.Tag;
}
if (request.Quantity.HasValue)
{
// For combo orders, the updated quantity is the quantity of the group
GroupOrderManager.Quantity = request.Quantity.Value;
}
}
}
}
+61
View File
@@ -0,0 +1,61 @@
/*
* 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.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to EZE brokerage
/// </summary>
public class EzeOrderProperties : OrderProperties
{
/// <summary>
/// Gets or sets the route name as shown in Eze EMS.
/// </summary>
public string Route { get; set; }
/// <summary>
/// Gets or sets a semi-colon separated list of trade or neutral accounts
/// the user has permission for, e.g., "TAL;TEST;USER1;TRADE" or "TAL;TEST;USER2;NEUTRAL".
/// </summary>
public string Account { get; set; }
/// <summary>
/// Gets or sets the user message or notes.
/// </summary>
public string Notes { get; set; }
/// <summary>
/// Gets or sets the account type for the order.
/// </summary>
/// <remarks>Should be set to <b>"119"</b> for margin orders in Eze EMS.</remarks>
public string AccountType { get; set; }
/// <summary>
/// Initializes a new instance with optional route, account, and notes.
/// </summary>
/// <param name="route">The trading route name (optional).</param>
/// <param name="account">The trading account with specific permissions (optional).</param>
/// <param name="notes">Optional notes about the order.</param>
/// <param name="accountType">The account type for the order (e.g., "119" for margin orders) (optional).</param>
public EzeOrderProperties(string route = default, string account = default, string notes = default, string accountType = default)
: base()
{
Route = route;
Account = account;
Notes = notes;
AccountType = accountType;
}
}
}
+46
View File
@@ -0,0 +1,46 @@
/*
* 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.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to FTX brokerage
/// </summary>
public class FTXOrderProperties : OrderProperties
{
/// <summary>
/// This flag will ensure the order executes only as a maker (maker fee) order.
/// If part of the order results in taking liquidity rather than providing,
/// it will be rejected and no part of the order will execute.
/// Note: this flag is only applied to Limit orders.
/// </summary>
public bool PostOnly { get; set; }
/// <summary>
/// If you send a reduce only order, it will only trade if it would decrease your position size.
/// </summary>
public bool ReduceOnly { get; set; }
/// <summary>
/// Returns a new instance clone of this object
/// </summary>
public override IOrderProperties Clone()
{
return (FTXOrderProperties)MemberwiseClone();
}
}
}
+87
View File
@@ -0,0 +1,87 @@
/*
* 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 QuantConnect.Securities.Crypto;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents the fee model specific to Alpaca trading platform.
/// </summary>
/// <remarks>This class inherits from <see cref="FeeModel"/> and provides the fee structure for Alpaca trades.
/// It implements the <see cref="IFeeModel"/> interface and should be used for calculating fees on the Alpaca platform.</remarks>
public class AlpacaFeeModel : FeeModel
{
/// <summary>
/// The fee percentage for a maker transaction in cryptocurrency.
/// </summary>
/// <see href="https://docs.alpaca.markets/docs/crypto-fees"/>
public const decimal MakerCryptoFee = 0.0015m;
/// <summary>
/// The fee percentage for a taker transaction in cryptocurrency.
/// </summary>
public const decimal TakerCryptoFee = 0.0025m;
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var security = parameters.Security;
if (security.Symbol.ID.SecurityType == SecurityType.Crypto)
{
var order = parameters.Order;
var fee = GetFee(order, MakerCryptoFee, TakerCryptoFee);
CashAmount cashAmount;
Crypto.DecomposeCurrencyPair(security.Symbol, security.SymbolProperties, out var baseCurrency, out var quoteCurrency);
if (order.Direction == OrderDirection.Buy)
{
// base currency, deducted from what we bought
cashAmount = new CashAmount(order.AbsoluteQuantity * fee, baseCurrency);
}
else
{
// quote currency
var positionValue = order.AbsoluteQuantity * security.Price;
cashAmount = new CashAmount(positionValue * fee, quoteCurrency);
}
return new OrderFee(cashAmount);
}
return new OrderFee(new CashAmount(0, Currencies.USD));
}
/// <summary>
/// Calculates the fee for a given order based on whether it is a maker or taker order.
/// </summary>
/// <param name="order">The order for which the fee is being calculated.</param>
/// <param name="makerFee">The fee percentage for maker orders.</param>
/// <param name="takerFee">The fee percentage for taker orders.</param>
/// <returns>The calculated fee for the given order.</returns>
private static decimal GetFee(Order order, decimal makerFee, decimal takerFee)
{
if (order.Type == OrderType.Limit && !order.IsMarketable)
{
return makerFee;
}
return takerFee;
}
}
}
+161
View File
@@ -0,0 +1,161 @@
/*
* 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.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models order fees that alpha stream clients pay/receive
/// </summary>
public class AlphaStreamsFeeModel : FeeModel
{
private readonly Dictionary<string, EquityFee> _equityFee =
new Dictionary<string, EquityFee> {
{ Market.USA, new EquityFee("USD", feePerShare: 0.005m, minimumFee: 1, maximumFeeRate: 0.005m) }
};
private readonly IDictionary<SecurityType, decimal> _feeRates = new Dictionary<SecurityType, decimal>
{
// Commission
{SecurityType.Forex, 0.000002m},
// Commission plus clearing fee
{SecurityType.Future, 0.4m + 0.1m},
{SecurityType.FutureOption, 0.4m + 0.1m},
{SecurityType.Option, 0.4m + 0.1m},
{SecurityType.IndexOption, 0.4m + 0.1m},
{SecurityType.Cfd, 0m}
};
private const decimal _makerFee = 0.001m;
private const decimal _takerFee = 0.002m;
/// <summary>
/// Gets the order fee associated with the specified order. This returns the cost
/// of the transaction in the account currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in units of the account currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
// Option exercise is free of charge
if (order.Type == OrderType.OptionExercise)
{
return OrderFee.Zero;
}
var market = security.Symbol.ID.Market;
decimal feeRate;
switch (security.Type)
{
case SecurityType.Option:
case SecurityType.Future:
case SecurityType.FutureOption:
case SecurityType.Cfd:
_feeRates.TryGetValue(security.Type, out feeRate);
return new OrderFee(new CashAmount(feeRate * order.AbsoluteQuantity, Currencies.USD));
case SecurityType.Forex:
_feeRates.TryGetValue(security.Type, out feeRate);
return new OrderFee(new CashAmount(feeRate * Math.Abs(order.GetValue(security)), Currencies.USD));
case SecurityType.Crypto:
decimal fee = _takerFee;
var props = order.Properties as BitfinexOrderProperties;
if (order.Type == OrderType.Limit &&
props?.Hidden != true &&
(props?.PostOnly == true || !order.IsMarketable))
{
// limit order posted to the order book
fee = _makerFee;
}
// get order value in quote currency
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
if (order.Type == OrderType.Limit)
{
// limit order posted to the order book
unitPrice = ((LimitOrder)order).LimitPrice;
}
unitPrice *= security.SymbolProperties.ContractMultiplier;
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
return new OrderFee(new CashAmount(
unitPrice * order.AbsoluteQuantity * fee,
security.QuoteCurrency.Symbol));
// Use the IB fee model
case SecurityType.Equity:
EquityFee equityFee;
if (!_equityFee.TryGetValue(market, out equityFee))
{
throw new KeyNotFoundException(Messages.AlphaStreamsFeeModel.UnexpectedEquityMarket(market));
}
var tradeValue = Math.Abs(order.GetValue(security));
//Per share fees
var tradeFee = equityFee.FeePerShare * order.AbsoluteQuantity;
//Maximum Per Order: equityFee.MaximumFeeRate
//Minimum per order. $equityFee.MinimumFee
var maximumPerOrder = equityFee.MaximumFeeRate * tradeValue;
if (tradeFee < equityFee.MinimumFee)
{
tradeFee = equityFee.MinimumFee;
}
else if (tradeFee > maximumPerOrder)
{
tradeFee = maximumPerOrder;
}
return new OrderFee(new CashAmount(Math.Abs(tradeFee), equityFee.Currency));
default:
// unsupported security type
throw new ArgumentException(Messages.FeeModel.UnsupportedSecurityType(security));
}
}
/// <summary>
/// Helper class to handle Equity fees
/// </summary>
private class EquityFee
{
public string Currency { get; }
public decimal FeePerShare { get; }
public decimal MinimumFee { get; }
public decimal MaximumFeeRate { get; }
public EquityFee(string currency,
decimal feePerShare,
decimal minimumFee,
decimal maximumFeeRate)
{
Currency = currency;
FeePerShare = feePerShare;
MinimumFee = minimumFee;
MaximumFeeRate = maximumFeeRate;
}
}
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Axos order fees
/// </summary>
public class AxosFeeModel: IFeeModel
{
private readonly decimal _feesPerShare;
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="feesPerShare">The fees per share to apply</param>
/// <remarks>Default value is $0.0015 per share</remarks>
public AxosFeeModel(decimal? feesPerShare = null)
{
_feesPerShare = feesPerShare ?? 0.0035m;
}
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public OrderFee GetOrderFee(OrderFeeParameters parameters)
{
return new OrderFee(new CashAmount(_feesPerShare * parameters.Order.AbsoluteQuantity, Currencies.USD));
}
}
}
@@ -0,0 +1,45 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Binance Coin Futures order fees
/// </summary>
public class BinanceCoinFuturesFeeModel : BinanceFeeModel
{
/// <summary>
/// Tier 1 maker fees
/// https://www.binance.com/en/fee/deliveryFee
/// </summary>
public new const decimal MakerTier1Fee = 0.0001m;
/// <summary>
/// Tier 1 taker fees
/// https://www.binance.com/en/fee/deliveryFee
/// </summary>
public new const decimal TakerTier1Fee = 0.0005m;
/// <summary>
/// Creates Binance Coin Futures fee model setting fees values
/// </summary>
/// <param name="mFee">Maker fee value</param>
/// <param name="tFee">Taker fee value</param>
public BinanceCoinFuturesFeeModel(decimal mFee = MakerTier1Fee, decimal tFee = TakerTier1Fee)
: base(mFee, tFee)
{
}
}
}
+120
View File
@@ -0,0 +1,120 @@
/*
* 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.Util;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Binance order fees
/// </summary>
public class BinanceFeeModel : FeeModel
{
/// <summary>
/// Tier 1 maker fees
/// https://www.binance.com/en/fee/schedule
/// </summary>
public const decimal MakerTier1Fee = 0.001m;
/// <summary>
/// Tier 1 taker fees
/// https://www.binance.com/en/fee/schedule
/// </summary>
public const decimal TakerTier1Fee = 0.001m;
private readonly decimal _makerFee;
private readonly decimal _takerFee;
/// <summary>
/// Creates Binance fee model setting fees values
/// </summary>
/// <param name="mFee">Maker fee value</param>
/// <param name="tFee">Taker fee value</param>
public BinanceFeeModel(decimal mFee = MakerTier1Fee, decimal tFee = TakerTier1Fee)
{
_makerFee = mFee;
_takerFee = tFee;
}
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var security = parameters.Security;
var order = parameters.Order;
var fee = GetFee(order);
if(security.Symbol.ID.SecurityType == SecurityType.CryptoFuture)
{
var positionValue = security.Holdings.GetQuantityValue(order.AbsoluteQuantity, security.Price);
return new OrderFee(new CashAmount(positionValue.Amount * fee, positionValue.Cash.Symbol));
}
if (order.Direction == OrderDirection.Buy)
{
// fees taken in the received currency
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var baseCurrency, out _);
return new OrderFee(new CashAmount(order.AbsoluteQuantity * fee, baseCurrency));
}
// get order value in quote currency
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
if (order.Type == OrderType.Limit)
{
// limit order posted to the order book
unitPrice = ((LimitOrder)order).LimitPrice;
}
unitPrice *= security.SymbolProperties.ContractMultiplier;
return new OrderFee(new CashAmount(
unitPrice * order.AbsoluteQuantity * fee,
security.QuoteCurrency.Symbol));
}
/// <summary>
/// Gets the fee factor for the given order
/// </summary>
/// <param name="order">The order to get the fee factor for</param>
/// <returns>The fee factor for the given order</returns>
protected virtual decimal GetFee(Order order)
{
return GetFee(order, _makerFee, _takerFee);
}
/// <summary>
/// Gets the fee factor for the given order taking into account the maker and the taker fee
/// </summary>
protected static decimal GetFee(Order order, decimal makerFee, decimal takerFee)
{
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
var fee = takerFee;
var props = order.Properties as BinanceOrderProperties;
if (order.Type == OrderType.Limit && ((props != null && props.PostOnly) || !order.IsMarketable))
{
// limit order posted to the order book
fee = makerFee;
}
return fee;
}
}
}
@@ -0,0 +1,88 @@
/*
* 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.Util;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Binance Futures order fees
/// </summary>
public class BinanceFuturesFeeModel : BinanceFeeModel
{
/// <summary>
/// Tier 1 USDT maker fees
/// https://www.binance.com/en/fee/futureFee
/// </summary>
public const decimal MakerTier1USDTFee = 0.0002m;
/// <summary>
/// Tier 1 USDT taker fees
/// https://www.binance.com/en/fee/futureFee
/// </summary>
public const decimal TakerTier1USDTFee = 0.0004m;
/// <summary>
/// Tier 1 BUSD maker fees
/// https://www.binance.com/en/fee/futureFee
/// </summary>
public const decimal MakerTier1BUSDFee = 0.00012m;
/// <summary>
/// Tier 1 BUSD taker fees
/// https://www.binance.com/en/fee/futureFee
/// </summary>
public const decimal TakerTier1BUSDFee = 0.00036m;
private decimal _makerUsdtFee;
private decimal _takerUsdtFee;
private decimal _makerBusdFee;
private decimal _takerBusdFee;
/// <summary>
/// Creates Binance Futures fee model setting fees values
/// </summary>
/// <param name="mUsdtFee">Maker fee value for USDT pair contracts</param>
/// <param name="tUsdtFee">Taker fee value for USDT pair contracts</param>
/// <param name="mBusdFee">Maker fee value for BUSD pair contracts</param>
/// <param name="tBusdFee">Taker fee value for BUSD pair contracts</param>
public BinanceFuturesFeeModel(decimal mUsdtFee = MakerTier1USDTFee, decimal tUsdtFee = TakerTier1USDTFee,
decimal mBusdFee = MakerTier1BUSDFee, decimal tBusdFee = TakerTier1BUSDFee)
: base(-1, -1)
{
_makerUsdtFee = mUsdtFee;
_takerUsdtFee = tUsdtFee;
_makerBusdFee = mBusdFee;
_takerBusdFee = tBusdFee;
}
/// <summary>
/// Gets the fee for the given order
/// </summary>
protected override decimal GetFee(Order order)
{
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var _, out var quoteCurrency);
var makerFee = _makerUsdtFee;
var takerFee = _takerUsdtFee;
if (quoteCurrency == "BUSD")
{
makerFee = _makerBusdFee;
takerFee = _takerBusdFee;
}
return GetFee(order, makerFee, takerFee);
}
}
}
+84
View File
@@ -0,0 +1,84 @@
/*
* 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.Util;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Bitfinex order fees
/// </summary>
public class BitfinexFeeModel : FeeModel
{
/// <summary>
/// Tier 1 maker fees
/// Maker fees are paid when you add liquidity to our order book by placing a limit order under the ticker price for buy and above the ticker price for sell.
/// https://www.bitfinex.com/fees
/// </summary>
public const decimal MakerFee = 0.001m;
/// <summary>
/// Tier 1 taker fees
/// Taker fees are paid when you remove liquidity from our order book by placing any order that is executed against an order of the order book.
/// Note: If you place a hidden order, you will always pay the taker fee. If you place a limit order that hits a hidden order, you will always pay the maker fee.
/// https://www.bitfinex.com/fees
/// </summary>
public const decimal TakerFee = 0.002m;
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
var fee = TakerFee;
var props = order.Properties as BitfinexOrderProperties;
if (order.Type == OrderType.Limit &&
props?.Hidden != true &&
(props?.PostOnly == true || !order.IsMarketable))
{
// limit order posted to the order book
fee = MakerFee;
}
if (order.Direction == OrderDirection.Buy)
{
// fees taken in the received currency
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var baseCurrency, out _);
return new OrderFee(new CashAmount(order.AbsoluteQuantity * fee, baseCurrency));
}
// get order value in quote currency
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
if (order.Type == OrderType.Limit)
{
// limit order posted to the order book
unitPrice = ((LimitOrder)order).LimitPrice;
}
unitPrice *= security.SymbolProperties.ContractMultiplier;
return new OrderFee(new CashAmount(
unitPrice * order.AbsoluteQuantity * fee,
security.QuoteCurrency.Symbol));
}
}
}
+117
View File
@@ -0,0 +1,117 @@
/*
* 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 QuantConnect.Util;
namespace QuantConnect.Orders.Fees;
/// <summary>
/// Bybit fee model implementation
/// </summary>
public class BybitFeeModel : FeeModel
{
/// <summary>
/// Tier 1 maker fees
/// https://learn.bybit.com/bybit-guide/bybit-trading-fees/
/// </summary>
public const decimal MakerNonVIPFee = 0.001m;
/// <summary>
/// Tier 1 taker fees
/// https://learn.bybit.com/bybit-guide/bybit-trading-fees/
/// </summary>
public const decimal TakerNonVIPFee = 0.001m;
private readonly decimal _makerFee;
private readonly decimal _takerFee;
/// <summary>
/// Creates Binance fee model setting fees values
/// </summary>
/// <param name="mFee">Maker fee value</param>
/// <param name="tFee">Taker fee value</param>
public BybitFeeModel(decimal mFee = MakerNonVIPFee, decimal tFee = TakerNonVIPFee)
{
_makerFee = mFee;
_takerFee = tFee;
}
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var security = parameters.Security;
var order = parameters.Order;
var fee = GetFee(order);
if(security.Symbol.ID.SecurityType == SecurityType.CryptoFuture)
{
var positionValue = security.Holdings.GetQuantityValue(order.AbsoluteQuantity, security.Price);
return new OrderFee(new CashAmount(positionValue.Amount * fee, positionValue.Cash.Symbol));
}
if (order.Direction == OrderDirection.Buy)
{
// fees taken in the received currency
CurrencyPairUtil.DecomposeCurrencyPair(order.Symbol, out var baseCurrency, out _);
return new OrderFee(new CashAmount(order.AbsoluteQuantity * fee, baseCurrency));
}
// get order value in quote currency
var unitPrice = security.BidPrice;
if (order.Type == OrderType.Limit)
{
// limit order posted to the order book
unitPrice = ((LimitOrder)order).LimitPrice;
}
unitPrice *= security.SymbolProperties.ContractMultiplier;
return new OrderFee(new CashAmount(
unitPrice * order.AbsoluteQuantity * fee,
security.QuoteCurrency.Symbol));
}
/// <summary>
/// Gets the fee factor for the given order
/// </summary>
/// <param name="order">The order to get the fee factor for</param>
/// <returns>The fee factor for the given order</returns>
protected virtual decimal GetFee(Order order)
{
return GetFee(order, _makerFee, _takerFee);
}
private static decimal GetFee(Order order, decimal makerFee, decimal takerFee)
{
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
var fee = takerFee;
var props = order.Properties as BybitOrderProperties;
if (order.Type == OrderType.Limit && ((props != null && props.PostOnly) || !order.IsMarketable))
{
// limit order posted to the order book
fee = makerFee;
}
return fee;
}
}
@@ -0,0 +1,44 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fees;
/// <summary>
/// Bybit futures fee model implementation
/// </summary>
public class BybitFuturesFeeModel : BybitFeeModel
{
/// <summary>
/// Tier 1 maker fees
/// https://learn.bybit.com/bybit-guide/bybit-trading-fees/
/// </summary>
public new const decimal MakerNonVIPFee = 0.0002m;
/// <summary>
/// Tier 1 taker fees
/// https://learn.bybit.com/bybit-guide/bybit-trading-fees/
/// </summary>
public new const decimal TakerNonVIPFee = 0.00055m;
/// <summary>
/// Initializes a new instance of the <see cref="BybitFuturesFeeModel"/> class
/// </summary>
/// <param name="makerFee">The accounts maker fee</param>
/// <param name="takerFee">The accounts taker fee</param>
public BybitFuturesFeeModel(decimal makerFee = MakerNonVIPFee, decimal takerFee = TakerNonVIPFee)
: base(makerFee, takerFee)
{
}
}
@@ -0,0 +1,64 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a fee model specific to Charles Schwab.
/// </summary>
/// <see href="https://www.schwab.com/pricing"/>
public class CharlesSchwabFeeModel : FeeModel
{
/// <summary>
/// The exchange processing fee for standard option securities.
/// </summary>
private const decimal _optionIndexFee = 1m;
/// <summary>
/// Represents the fee associated with equity options transactions (per contract).
/// </summary>
private const decimal _optionFee = 0.65m;
/// <summary>
/// Calculates the order fee based on the security type and order parameters.
/// </summary>
/// <param name="parameters">The parameters for the order fee calculation, which include security and order details.</param>
/// <returns>
/// An <see cref="OrderFee"/> instance representing the calculated order fee.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="parameters"/> is <c>null</c>.
/// </exception>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
if (parameters.Security.Type.IsOption())
{
var feeRate = parameters.Security.Type switch
{
SecurityType.IndexOption => _optionIndexFee,
SecurityType.Option => _optionFee,
_ => 0m
};
return new OrderFee(new CashAmount(parameters.Order.AbsoluteQuantity * feeRate, Currencies.USD));
}
return OrderFee.Zero;
}
}
}
+137
View File
@@ -0,0 +1,137 @@
/*
* 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 QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a fee model specific to Coinbase.
/// This class extends the base fee model.
/// </summary>
public class CoinbaseFeeModel : FeeModel
{
/// <summary>
/// Level Advanced 1 maker fee
/// Tab "Fee tiers" on <see href="https://www.coinbase.com/advanced-fees"/>
/// </summary>
public const decimal MakerAdvanced1 = 0.006m;
/// <summary>
/// Level Advanced 1 taker fee
/// Tab "Fee tiers" on <see href="https://www.coinbase.com/advanced-fees"/>
/// </summary>
public const decimal TakerAdvanced1 = 0.008m;
/// <summary>
/// Stable Pairs maker fee
/// Tab "Stable pairs" on <see href="https://www.coinbase.com/advanced-fees"/>
/// </summary>
public const decimal MakerStablePairs = 0m;
/// <summary>
/// Stable Pairs taker fee
/// Tab "Stable pairs" on <see href="https://www.coinbase.com/advanced-fees"/>
/// </summary>
public const decimal TakerStableParis = 0.00001m;
private readonly decimal _makerFee;
private readonly decimal _takerFee;
/// <summary>
/// Create Coinbase Fee model setting fee values
/// </summary>
/// <param name="makerFee">Maker fee value</param>
/// <param name="takerFee">Taker fee value</param>
/// <remarks>By default: use Level Advanced 1 fees</remarks>
public CoinbaseFeeModel(decimal makerFee = MakerAdvanced1, decimal takerFee = TakerAdvanced1)
{
_makerFee = makerFee;
_takerFee = takerFee;
}
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters), "The 'parameters' argument cannot be null.");
}
var order = parameters.Order;
var security = parameters.Security;
var props = order.Properties as CoinbaseOrderProperties;
// marketable limit orders are considered takers
var isMaker = order.Type == OrderType.Limit && ((props != null && props.PostOnly) || !order.IsMarketable);
// Check if the current symbol is a StableCoin
var isStableCoin = Currencies.StablePairsCoinbase.Contains(security.Symbol.Value);
var feePercentage = GetFeePercentage(order.Time, isMaker, isStableCoin, _makerFee, _takerFee);
// get order value in quote currency, then apply maker/taker fee factor
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
unitPrice *= security.SymbolProperties.ContractMultiplier;
// currently we do not model 30-day volume, so we use the first tier
var fee = unitPrice * order.AbsoluteQuantity * feePercentage;
return new OrderFee(new CashAmount(fee, security.QuoteCurrency.Symbol));
}
/// <summary>
/// Returns the maker/taker fee percentage effective at the requested date.
/// </summary>
/// <param name="utcTime">The date/time requested (UTC)</param>
/// <param name="isMaker">true if the maker percentage fee is requested, false otherwise</param>
/// <param name="isStableCoin">true if the order security symbol is a StableCoin, false otherwise</param>
/// <param name="makerFee">maker fee amount</param>
/// <param name="takerFee">taker fee amount</param>
/// <returns>The fee percentage</returns>
protected static decimal GetFeePercentage(DateTime utcTime, bool isMaker, bool isStableCoin, decimal makerFee, decimal takerFee)
{
if (isStableCoin && utcTime < new DateTime(2022, 6, 1))
{
return isMaker ? 0m : 0.001m;
}
else if(isStableCoin)
{
return isMaker ? MakerStablePairs : TakerStableParis;
}
else if (utcTime < new DateTime(2019, 3, 23, 1, 30, 0))
{
return isMaker ? 0m : 0.003m;
}
else if (utcTime < new DateTime(2019, 10, 8, 0, 30, 0))
{
return isMaker ? 0.0015m : 0.0025m;
}
// https://www.coinbase.com/advanced-fees
// Level | Trading amount | Spot fees (Maker | Taker)
// Advanced 1 | >= $0 | 0.60% | 0.80%
return isMaker ? makerFee : takerFee;
}
}
}
+51
View File
@@ -0,0 +1,51 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an order fee model that always returns the same order fee.
/// </summary>
public class ConstantFeeModel : FeeModel
{
private readonly decimal _fee;
private readonly string _currency;
/// <summary>
/// Initializes a new instance of the <see cref="ConstantFeeModel"/> class with the specified <paramref name="fee"/>
/// </summary>
/// <param name="fee">The constant order fee used by the model</param>
/// <param name="currency">The currency of the order fee</param>
public ConstantFeeModel(decimal fee, string currency = "USD")
{
_fee = Math.Abs(fee);
_currency = currency;
}
/// <summary>
/// Returns the constant fee for the model in units of the account currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in units of the account currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
return new OrderFee(new CashAmount(_fee, _currency));
}
}
}
+130
View File
@@ -0,0 +1,130 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Exante order fees.
/// According to:
/// <list type="bullet">
/// <item>https://support.exante.eu/hc/en-us/articles/115005873143-Fees-overview-exchange-imposed-fees?source=search</item>
/// <item>https://exante.eu/markets/</item>
/// </list>
/// </summary>
public class ExanteFeeModel : FeeModel
{
/// <summary>
/// Market USA rate
/// </summary>
public const decimal MarketUsaRate = 0.02m;
/// <summary>
/// Default rate
/// </summary>
public const decimal DefaultRate = 0.02m;
private readonly decimal _forexCommissionRate;
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="forexCommissionRate">Commission rate for FX operations</param>
public ExanteFeeModel(decimal forexCommissionRate = 0.25m)
{
_forexCommissionRate = forexCommissionRate;
}
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
decimal feeResult;
string feeCurrency;
switch (security.Type)
{
case SecurityType.Forex:
var totalOrderValue = order.GetValue(security);
feeResult = Math.Abs(_forexCommissionRate * totalOrderValue);
feeCurrency = Currencies.USD;
break;
case SecurityType.Equity:
var equityFee = ComputeEquityFee(order);
feeResult = equityFee.Amount;
feeCurrency = equityFee.Currency;
break;
case SecurityType.Option:
case SecurityType.IndexOption:
var optionsFee = ComputeOptionFee(order);
feeResult = optionsFee.Amount;
feeCurrency = optionsFee.Currency;
break;
case SecurityType.Future:
case SecurityType.FutureOption:
feeResult = 1.5m;
feeCurrency = Currencies.USD;
break;
default:
throw new ArgumentException(Messages.FeeModel.UnsupportedSecurityType(security));
}
return new OrderFee(new CashAmount(feeResult, feeCurrency));
}
/// <summary>
/// Computes fee for equity order
/// </summary>
/// <param name="order">LEAN order</param>
private static CashAmount ComputeEquityFee(Order order)
{
switch (order.Symbol.ID.Market)
{
case Market.USA:
return new CashAmount(order.AbsoluteQuantity * MarketUsaRate, Currencies.USD);
default:
return new CashAmount(order.AbsoluteQuantity * order.Price * DefaultRate, Currencies.USD);
}
}
/// <summary>
/// Computes fee for option order
/// </summary>
/// <param name="order">LEAN order</param>
private static CashAmount ComputeOptionFee(Order order)
{
return order.Symbol.ID.Market switch
{
Market.USA => new CashAmount(order.AbsoluteQuantity * 1.5m, Currencies.USD),
_ =>
// ToDo: clarify the value for different exchanges
throw new ArgumentException(Messages.ExanteFeeModel.UnsupportedExchange(order))
};
}
}
}
+24
View File
@@ -0,0 +1,24 @@
/*
* 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.
*/
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Eze fee model implementation
/// </summary>
public class EzeFeeModel : FeeModel
{
}
}
+71
View File
@@ -0,0 +1,71 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models FTX order fees
/// https://help.ftx.com/hc/en-us/articles/360024479432-Fees
/// </summary>
public class FTXFeeModel : FeeModel
{
/// <summary>
/// Tier 1 maker fees
/// </summary>
public virtual decimal MakerFee => 0.0002m;
/// <summary>
/// Tier 1 taker fees
/// </summary>
public virtual decimal TakerFee => 0.0007m;
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
var props = order.Properties as FTXOrderProperties;
//taker by default
var fee = TakerFee;
var unitPrice = order.Direction == OrderDirection.Buy ? security.AskPrice : security.BidPrice;
unitPrice *= security.SymbolProperties.ContractMultiplier;
var currency = security.QuoteCurrency.Symbol;
//maker if limit
if (order.Type == OrderType.Limit && (props?.PostOnly == true || !order.IsMarketable))
{
fee = MakerFee;
if (order.Direction == OrderDirection.Buy)
{
unitPrice = 1;
currency = ((IBaseCurrencySymbol)security).BaseCurrency.Symbol;
}
}
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
return new OrderFee(new CashAmount(
unitPrice * order.AbsoluteQuantity * fee,
currency));
}
}
}
+34
View File
@@ -0,0 +1,34 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models FTX order fees
/// https://help.ftx.us/hc/en-us/articles/360043579273-Fees
/// </summary>
public class FTXUSFeeModel : FTXFeeModel
{
/// <summary>
/// Tier 1 maker fees
/// </summary>
public override decimal MakerFee => 0.001m;
/// <summary>
/// Tier 1 taker fees
/// </summary>
public override decimal TakerFee => 0.004m;
}
}
+40
View File
@@ -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 QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Base class for any order fee model
/// </summary>
/// <remarks>Please use <see cref="FeeModel"/> as the base class for
/// any implementations of <see cref="IFeeModel"/></remarks>
public class FeeModel : IFeeModel
{
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
public virtual OrderFee GetOrderFee(OrderFeeParameters parameters)
{
return new OrderFee(new CashAmount(
0,
"USD"));
}
}
}
+76
View File
@@ -0,0 +1,76 @@
/*
* 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.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models FXCM order fees
/// </summary>
public class FxcmFeeModel : FeeModel
{
private readonly string _currency;
private readonly HashSet<Symbol> _groupCommissionSchedule1 = new HashSet<Symbol>
{
Symbol.Create("EURUSD", SecurityType.Forex, Market.FXCM),
Symbol.Create("GBPUSD", SecurityType.Forex, Market.FXCM),
Symbol.Create("USDJPY", SecurityType.Forex, Market.FXCM),
Symbol.Create("USDCHF", SecurityType.Forex, Market.FXCM),
Symbol.Create("AUDUSD", SecurityType.Forex, Market.FXCM),
Symbol.Create("EURJPY", SecurityType.Forex, Market.FXCM),
Symbol.Create("GBPJPY", SecurityType.Forex, Market.FXCM),
};
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="currency">The currency of the order fee, for FXCM this is the account currency</param>
public FxcmFeeModel(string currency = "USD")
{
_currency = currency;
}
/// <summary>
/// Get the fee for this order in units of the account currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in units of the account currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
// From http://www.fxcm.com/forex/forex-pricing/ (on Oct 6th, 2015)
// Forex: $0.04 per side per 1k lot for EURUSD, GBPUSD, USDJPY, USDCHF, AUDUSD, EURJPY, GBPJPY
// $0.06 per side per 1k lot for other instruments
// From https://www.fxcm.com/uk/markets/cfds/frequently-asked-questions/
// CFD: no commissions
decimal fee = 0;
if (parameters.Security.Type == SecurityType.Forex)
{
var commissionRate = _groupCommissionSchedule1.Contains(parameters.Security.Symbol)
? 0.04m : 0.06m;
fee = Math.Abs(commissionRate * parameters.Order.AbsoluteQuantity / 1000);
}
return new OrderFee(new CashAmount(fee,
_currency));
}
}
}
+26
View File
@@ -0,0 +1,26 @@
/*
* 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.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models GDAX order fees
/// </summary>
[Obsolete("GDAXFeeModel is deprecated. Use CoinbaseFeeModel instead.")]
public class GDAXFeeModel : CoinbaseFeeModel
{ }
}
+58
View File
@@ -0,0 +1,58 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a model the simulates order fees
/// </summary>
/// <remarks>Please use <see cref="FeeModel"/> as the base class for
/// any implementations of <see cref="IFeeModel"/></remarks>
public interface IFeeModel
{
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
OrderFee GetOrderFee(OrderFeeParameters parameters);
}
/// <summary>
/// Provide extension method for <see cref="IFeeModel"/> to enable
/// backwards compatibility of invocations.
/// </summary>
public static class FeeModelExtensions
{
/// <summary>
/// Gets the order fee associated with the specified order. This returns the cost
/// of the transaction in the account currency
/// </summary>
/// <param name="model">The fee model</param>
/// <param name="security">The security matching the order</param>
/// <param name="order">The order to compute fees for</param>
/// <returns>The cost of the order in units of the account currency</returns>
public static decimal GetOrderFee(this IFeeModel model, Security security, Order order)
{
var parameters = new OrderFeeParameters(security, order);
var fee = model.GetOrderFee(parameters);
return fee.Value.Amount;
}
}
}
+123
View File
@@ -0,0 +1,123 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides the default implementation of <see cref="IFeeModel"/> Refer to https://www.samco.in/technology/brokerage_calculator
/// </summary>
public class IndiaFeeModel : IFeeModel
{
/// <summary>
/// Brokerage calculation Factor
/// </summary>
protected virtual decimal BrokerageMultiplier { get; set; }
/// <summary>
/// Maximum brokerage per order
/// </summary>
protected virtual decimal MaxBrokerage { get; set; }
/// <summary>
/// Securities Transaction Tax calculation Factor
/// </summary>
protected virtual decimal SecuritiesTransactionTaxTotalMultiplier { get; set; }
/// <summary>
/// Exchange Transaction Charge calculation Factor
/// </summary>
protected virtual decimal ExchangeTransactionChargeMultiplier { get; set; }
/// <summary>
/// State Tax calculation Factor
/// </summary>
protected virtual decimal StateTaxMultiplier { get; set; }
/// <summary>
/// Sebi Charges calculation Factor
/// </summary>
protected virtual decimal SebiChargesMultiplier { get; set; }
/// <summary>
/// Stamp Charges calculation Factor
/// </summary>
protected virtual decimal StampChargesMultiplier { get; set; }
/// <summary>
/// Checks if Stamp Charges is calculated from order valur or turnover
/// </summary>
protected virtual bool IsStampChargesFromOrderValue { get; set; }
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">
/// A <see cref="OrderFeeParameters"/> object containing the security and order
/// </param>
public OrderFee GetOrderFee(OrderFeeParameters parameters)
{
if (parameters.Security == null)
{
return OrderFee.Zero;
}
var orderValue = parameters.Order.GetValue(parameters.Security);
var fee = GetFee(orderValue);
return new OrderFee(new CashAmount(fee, Currencies.INR));
}
private decimal GetFee(decimal orderValue)
{
bool isSell = orderValue < 0;
orderValue = Math.Abs(orderValue);
var multiplied = orderValue * BrokerageMultiplier;
var brokerage = (multiplied > MaxBrokerage) ? MaxBrokerage : Math.Round(multiplied, 2);
var turnover = Math.Round(orderValue, 2);
decimal securitiesTransactionTaxTotal = 0;
if (isSell)
{
securitiesTransactionTaxTotal = Math.Round(orderValue * SecuritiesTransactionTaxTotalMultiplier, 2);
}
var exchangeTransactionCharge = Math.Round(turnover * ExchangeTransactionChargeMultiplier, 2);
var clearingCharge = 0;
var stateTax = Math.Round(StateTaxMultiplier * (brokerage + exchangeTransactionCharge), 2);
var sebiCharges = Math.Round((turnover * SebiChargesMultiplier), 2);
decimal stampCharges = 0;
if (!isSell)
{
if (IsStampChargesFromOrderValue)
{
stampCharges = Math.Round((orderValue * StampChargesMultiplier), 2);
}
else
{
stampCharges = Math.Round((turnover * StampChargesMultiplier), 2);
}
}
var totalTax = Math.Round(brokerage + securitiesTransactionTaxTotal + exchangeTransactionCharge + stampCharges + clearingCharge + stateTax + sebiCharges, 2);
return totalTax;
}
}
}
@@ -0,0 +1,502 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Securities;
using QuantConnect.Orders.Fills;
using System.Collections.Generic;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides the default implementation of <see cref="IFeeModel"/>
/// </summary>
public class InteractiveBrokersFeeModel : FeeModel
{
private readonly decimal _forexCommissionRate;
private readonly decimal _forexMinimumOrderFee;
// option commission function takes number of contracts and the size of the option premium and returns total commission
private readonly Dictionary<string, Func<decimal, decimal, CashAmount>> _optionFee =
new Dictionary<string, Func<decimal, decimal, CashAmount>>();
#pragma warning disable CS1570
/// <summary>
/// Reference at https://www.interactivebrokers.com/en/index.php?f=commission&p=futures1
/// </summary>
#pragma warning restore CS1570
private readonly Dictionary<string, Func<Security, CashAmount>> _futureFee =
// IB fee + exchange fee
new()
{
{ Market.USA, UnitedStatesFutureFees },
{ Market.HKFE, HongKongFutureFees },
{ Market.EUREX, EUREXFutureFees }
};
/// <summary>
/// Initializes a new instance of the <see cref="ImmediateFillModel"/>
/// </summary>
/// <param name="monthlyForexTradeAmountInUSDollars">Monthly FX dollar volume traded</param>
/// <param name="monthlyOptionsTradeAmountInContracts">Monthly options contracts traded</param>
public InteractiveBrokersFeeModel(decimal monthlyForexTradeAmountInUSDollars = 0, decimal monthlyOptionsTradeAmountInContracts = 0)
{
ProcessForexRateSchedule(monthlyForexTradeAmountInUSDollars, out _forexCommissionRate, out _forexMinimumOrderFee);
Func<decimal, decimal, CashAmount> optionsCommissionFunc;
ProcessOptionsRateSchedule(monthlyOptionsTradeAmountInContracts, out optionsCommissionFunc);
// only USA for now
_optionFee.Add(Market.USA, optionsCommissionFunc);
}
/// <summary>
/// Gets the order fee associated with the specified order. This returns the cost
/// of the transaction in the account currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in units of the account currency</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
// Option exercise for equity options is free of charge
if (order.Type == OrderType.OptionExercise)
{
var optionOrder = (OptionExerciseOrder)order;
// For Futures Options, contracts are charged the standard commission at expiration of the contract.
// Read more here: https://www1.interactivebrokers.com/en/index.php?f=14718#trading-related-fees
if (optionOrder.Symbol.ID.SecurityType == SecurityType.Option)
{
return OrderFee.Zero;
}
}
var quantity = order.AbsoluteQuantity;
decimal feeResult;
string feeCurrency;
var market = security.Symbol.ID.Market;
switch (security.Type)
{
case SecurityType.Forex:
// get the total order value in the account currency
var totalOrderValue = order.GetValue(security);
var fee = Math.Abs(_forexCommissionRate*totalOrderValue);
feeResult = Math.Max(_forexMinimumOrderFee, fee);
// IB Forex fees are all in USD
feeCurrency = Currencies.USD;
break;
case SecurityType.Option:
case SecurityType.IndexOption:
Func<decimal, decimal, CashAmount> optionsCommissionFunc;
if (!_optionFee.TryGetValue(market, out optionsCommissionFunc))
{
throw new KeyNotFoundException(Messages.InteractiveBrokersFeeModel.UnexpectedOptionMarket(market));
}
// applying commission function to the order
var optionFee = optionsCommissionFunc(quantity, GetPotentialOrderPrice(order, security));
feeResult = optionFee.Amount;
feeCurrency = optionFee.Currency;
break;
case SecurityType.Future:
case SecurityType.FutureOption:
// The futures options fee model is exactly the same as futures' fees on IB.
if (market == Market.Globex || market == Market.NYMEX
|| market == Market.CBOT || market == Market.ICE
|| market == Market.CFE || market == Market.COMEX
|| market == Market.CME || market == Market.NYSELIFFE)
{
// just in case...
market = Market.USA;
}
if (!_futureFee.TryGetValue(market, out var feeRatePerContractFunc))
{
throw new KeyNotFoundException(Messages.InteractiveBrokersFeeModel.UnexpectedFutureMarket(market));
}
var feeRatePerContract = feeRatePerContractFunc(security);
feeResult = quantity * feeRatePerContract.Amount;
feeCurrency = feeRatePerContract.Currency;
break;
case SecurityType.Equity:
EquityFee equityFee;
switch (market)
{
case Market.USA:
equityFee = new EquityFee(Currencies.USD, feePerShare: 0.005m, minimumFee: 1, maximumFeeRate: 0.005m);
break;
case Market.India:
equityFee = new EquityFee(Currencies.INR, feePerShare: 0.01m, minimumFee: 6, maximumFeeRate: 20);
break;
default:
throw new KeyNotFoundException(Messages.InteractiveBrokersFeeModel.UnexpectedEquityMarket(market));
}
var tradeValue = Math.Abs(order.GetValue(security));
//Per share fees
var tradeFee = equityFee.FeePerShare * quantity;
//Maximum Per Order: equityFee.MaximumFeeRate
//Minimum per order. $equityFee.MinimumFee
var maximumPerOrder = equityFee.MaximumFeeRate * tradeValue;
if (tradeFee < equityFee.MinimumFee)
{
tradeFee = equityFee.MinimumFee;
}
else if (tradeFee > maximumPerOrder)
{
tradeFee = maximumPerOrder;
}
feeCurrency = equityFee.Currency;
//Always return a positive fee.
feeResult = Math.Abs(tradeFee);
break;
case SecurityType.Cfd:
var value = Math.Abs(order.GetValue(security));
feeResult = 0.00002m * value; // 0.002%
feeCurrency = security.QuoteCurrency.Symbol;
var minimumFee = security.QuoteCurrency.Symbol switch
{
"JPY" => 40.0m,
"HKD" => 10.0m,
_ => 1.0m
};
feeResult = Math.Max(feeResult, minimumFee);
break;
default:
// unsupported security type
throw new ArgumentException(Messages.FeeModel.UnsupportedSecurityType(security));
}
return new OrderFee(new CashAmount(
feeResult,
feeCurrency));
}
/// <summary>
/// Approximates the order's price based on the order type
/// </summary>
protected static decimal GetPotentialOrderPrice(Order order, Security security)
{
decimal price = 0;
switch (order.Type)
{
case OrderType.TrailingStop:
price = (order as TrailingStopOrder).StopPrice;
break;
case OrderType.StopMarket:
price = (order as StopMarketOrder).StopPrice;
break;
case OrderType.ComboMarket:
case OrderType.MarketOnOpen:
case OrderType.MarketOnClose:
case OrderType.Market:
decimal securityPrice;
if (order.Direction == OrderDirection.Buy)
{
price = security.BidPrice;
}
else
{
price = security.AskPrice;
}
break;
case OrderType.ComboLimit:
price = (order as ComboLimitOrder).GroupOrderManager.LimitPrice;
break;
case OrderType.ComboLegLimit:
price = (order as ComboLegLimitOrder).LimitPrice;
break;
case OrderType.StopLimit:
price = (order as StopLimitOrder).LimitPrice;
break;
case OrderType.LimitIfTouched:
price = (order as LimitIfTouchedOrder).LimitPrice;
break;
case OrderType.Limit:
price = (order as LimitOrder).LimitPrice;
break;
}
return price;
}
/// <summary>
/// Determines which tier an account falls into based on the monthly trading volume
/// </summary>
private static void ProcessForexRateSchedule(decimal monthlyForexTradeAmountInUSDollars, out decimal commissionRate, out decimal minimumOrderFee)
{
const decimal bp = 0.0001m;
if (monthlyForexTradeAmountInUSDollars <= 1000000000) // 1 billion
{
commissionRate = 0.20m * bp;
minimumOrderFee = 2.00m;
}
else if (monthlyForexTradeAmountInUSDollars <= 2000000000) // 2 billion
{
commissionRate = 0.15m * bp;
minimumOrderFee = 1.50m;
}
else if (monthlyForexTradeAmountInUSDollars <= 5000000000) // 5 billion
{
commissionRate = 0.10m * bp;
minimumOrderFee = 1.25m;
}
else
{
commissionRate = 0.08m * bp;
minimumOrderFee = 1.00m;
}
}
/// <summary>
/// Determines which tier an account falls into based on the monthly trading volume
/// </summary>
private static void ProcessOptionsRateSchedule(decimal monthlyOptionsTradeAmountInContracts, out Func<decimal, decimal, CashAmount> optionsCommissionFunc)
{
if (monthlyOptionsTradeAmountInContracts <= 10000)
{
optionsCommissionFunc = (orderSize, premium) =>
{
var commissionRate = premium >= 0.1m ?
0.65m :
(0.05m <= premium && premium < 0.1m ? 0.5m : 0.25m);
return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
};
}
else if (monthlyOptionsTradeAmountInContracts <= 50000)
{
optionsCommissionFunc = (orderSize, premium) =>
{
var commissionRate = premium >= 0.05m ? 0.5m : 0.25m;
return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
};
}
else if (monthlyOptionsTradeAmountInContracts <= 100000)
{
optionsCommissionFunc = (orderSize, premium) =>
{
var commissionRate = 0.25m;
return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
};
}
else
{
optionsCommissionFunc = (orderSize, premium) =>
{
var commissionRate = 0.15m;
return new CashAmount(Math.Max(orderSize * commissionRate, 1.0m), Currencies.USD);
};
}
}
private static CashAmount UnitedStatesFutureFees(Security security)
{
IDictionary<string, decimal> fees, exchangeFees;
decimal ibFeePerContract, exchangeFeePerContract;
string symbol;
switch (security.Symbol.SecurityType)
{
case SecurityType.Future:
fees = _usaFuturesFees;
exchangeFees = _usaFuturesExchangeFees;
symbol = security.Symbol.ID.Symbol;
break;
case SecurityType.FutureOption:
fees = _usaFutureOptionsFees;
exchangeFees = _usaFutureOptionsExchangeFees;
symbol = security.Symbol.Underlying.ID.Symbol;
break;
default:
throw new ArgumentException(Messages.InteractiveBrokersFeeModel.UnitedStatesFutureFeesUnsupportedSecurityType(security));
}
if (!fees.TryGetValue(symbol, out ibFeePerContract))
{
ibFeePerContract = 0.85m;
}
if (!exchangeFees.TryGetValue(symbol, out exchangeFeePerContract))
{
exchangeFeePerContract = 1.60m;
}
// Add exchange fees + IBKR regulatory fee (0.02)
return new CashAmount(ibFeePerContract + exchangeFeePerContract + 0.02m, Currencies.USD);
}
/// <summary>
/// See https://www.hkex.com.hk/Services/Rules-and-Forms-and-Fees/Fees/Listed-Derivatives/Trading/Transaction?sc_lang=en
/// </summary>
private static CashAmount HongKongFutureFees(Security security)
{
if (security.Symbol.ID.Symbol.Equals("HSI", StringComparison.InvariantCultureIgnoreCase))
{
// IB fee + exchange fee
return new CashAmount(30 + 10, Currencies.HKD);
}
decimal ibFeePerContract;
switch (security.QuoteCurrency.Symbol)
{
case Currencies.CNH:
ibFeePerContract = 13;
break;
case Currencies.HKD:
ibFeePerContract = 20;
break;
case Currencies.USD:
ibFeePerContract = 2.40m;
break;
default:
throw new ArgumentException(Messages.InteractiveBrokersFeeModel.HongKongFutureFeesUnexpectedQuoteCurrency(security));
}
// let's add a 50% extra charge for exchange fees
return new CashAmount(ibFeePerContract * 1.5m, security.QuoteCurrency.Symbol);
}
private static CashAmount EUREXFutureFees(Security security)
{
IDictionary<string, decimal> fees, exchangeFees;
decimal ibFeePerContract, exchangeFeePerContract;
string symbol;
switch (security.Symbol.SecurityType)
{
case SecurityType.Future:
fees = _eurexFuturesFees;
exchangeFees = _eurexFuturesExchangeFees;
symbol = security.Symbol.ID.Symbol;
break;
default:
throw new ArgumentException(Messages.InteractiveBrokersFeeModel.EUREXFutureFeesUnsupportedSecurityType(security));
}
if (!fees.TryGetValue(symbol, out ibFeePerContract))
{
ibFeePerContract = 1.00m;
}
if (!exchangeFees.TryGetValue(symbol, out exchangeFeePerContract))
{
exchangeFeePerContract = 0.00m;
}
// Add exchange fees + IBKR regulatory fee (0.02)
return new CashAmount(ibFeePerContract + exchangeFeePerContract + 0.02m, Currencies.EUR);
}
/// <summary>
/// Reference at https://www.interactivebrokers.com/en/pricing/commissions-futures.php?re=amer
/// </summary>
private static readonly Dictionary<string, decimal> _usaFuturesFees = new()
{
// Micro E-mini Futures
{ "MYM", 0.25m }, { "M2K", 0.25m }, { "MES", 0.25m }, { "MNQ", 0.25m }, { "2YY", 0.25m }, { "5YY", 0.25m }, { "10Y", 0.25m },
{ "30Y", 0.25m }, { "MCL", 0.25m }, { "MGC", 0.25m }, { "SIL", 0.25m },
// Cryptocurrency Futures
{ "BTC", 5m }, { "MBT", 2.25m }, { "ETH", 3m }, { "MET", 0.20m }, { "MIB", 2.25m }, { "MRB", 0.20m },
// E-mini FX (currencies) Futures
{ "E7", 0.50m }, { "J7", 0.50m },
// Micro E-mini FX (currencies) Futures
{ "M6E", 0.15m }, { "M6A", 0.15m }, { "M6B", 0.15m }, { "MCD", 0.15m }, { "MJY", 0.15m }, { "MSF", 0.15m }, { "M6J", 0.15m },
{ "MIR", 0.15m }, { "M6C", 0.15m }, { "M6S", 0.15m }, { "MNH", 0.15m },
};
/// <summary>
/// Reference at https://www.interactivebrokers.com/en/pricing/commissions-futures-europe.php?re=europe
/// </summary>
private static readonly Dictionary<string, decimal> _eurexFuturesFees = new()
{
// Futures
{ "FESX", 1.00m },
};
private static readonly Dictionary<string, decimal> _usaFutureOptionsFees = new()
{
// Micro E-mini Future Options
{ "MYM", 0.25m }, { "M2K", 0.25m }, { "MES", 0.25m }, { "MNQ", 0.25m }, { "2YY", 0.25m }, { "5YY", 0.25m }, { "10Y", 0.25m },
{ "30Y", 0.25m }, { "MCL", 0.25m }, { "MGC", 0.25m }, { "SIL", 0.25m },
// Cryptocurrency Future Options
{ "BTC", 5m }, { "MBT", 1.25m }, { "ETH", 3m }, { "MET", 0.10m }, { "MIB", 1.25m }, { "MRB", 0.10m }
};
private static readonly Dictionary<string, decimal> _usaFuturesExchangeFees = new()
{
// E-mini Futures
{ "ES", 1.28m }, { "NQ", 1.28m }, { "YM", 1.28m }, { "RTY", 1.28m }, { "EMD", 1.28m },
// Micro E-mini Futures
{ "MYM", 0.30m }, { "M2K", 0.30m }, { "MES", 0.30m }, { "MNQ", 0.30m }, { "2YY", 0.30m }, { "5YY", 0.30m }, { "10Y", 0.30m },
{ "30Y", 0.30m }, { "MCL", 0.30m }, { "MGC", 0.30m }, { "SIL", 0.30m },
// Cryptocurrency Futures
{ "BTC", 6m }, { "MBT", 2.5m }, { "ETH", 4m }, { "MET", 0.20m }, { "MIB", 2.5m }, { "MRB", 0.20m },
// E-mini FX (currencies) Futures
{ "E7", 0.85m }, { "J7", 0.85m },
// Micro E-mini FX (currencies) Futures
{ "M6E", 0.24m }, { "M6A", 0.24m }, { "M6B", 0.24m }, { "MCD", 0.24m }, { "MJY", 0.24m }, { "MSF", 0.24m }, { "M6J", 0.24m },
{ "MIR", 0.24m }, { "M6C", 0.24m }, { "M6S", 0.24m }, { "MNH", 0.24m },
};
private static readonly Dictionary<string, decimal> _eurexFuturesExchangeFees = new()
{
// Futures
{ "FESX", 0.00m },
};
private static readonly Dictionary<string, decimal> _usaFutureOptionsExchangeFees = new()
{
// E-mini Future Options
{ "ES", 0.55m }, { "NQ", 0.55m }, { "YM", 0.55m }, { "RTY", 0.55m }, { "EMD", 0.55m },
// Micro E-mini Future Options
{ "MYM", 0.20m }, { "M2K", 0.20m }, { "MES", 0.20m }, { "MNQ", 0.20m }, { "2YY", 0.20m }, { "5YY", 0.20m }, { "10Y", 0.20m },
{ "30Y", 0.20m }, { "MCL", 0.20m }, { "MGC", 0.20m }, { "SIL", 0.20m },
// Cryptocurrency Future Options
{ "BTC", 5m }, { "MBT", 2.5m }, { "ETH", 4m }, { "MET", 0.20m }, { "MIB", 2.5m }, { "MRB", 0.20m },
};
/// <summary>
/// Helper class to handle IB Equity fees
/// </summary>
private class EquityFee
{
public string Currency { get; }
public decimal FeePerShare { get; }
public decimal MinimumFee { get; }
public decimal MaximumFeeRate { get; }
public EquityFee(string currency,
decimal feePerShare,
decimal minimumFee,
decimal maximumFeeRate)
{
Currency = currency;
FeePerShare = feePerShare;
MinimumFee = minimumFee;
MaximumFeeRate = maximumFeeRate;
}
}
}
}
+113
View File
@@ -0,0 +1,113 @@
/*
* 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.Securities;
using QuantConnect.Util;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Kraken order fees
/// </summary>
public class KrakenFeeModel : FeeModel
{
/// <summary>
/// We don't use 30 day model, so using only tier1 fees.
/// https://www.kraken.com/features/fee-schedule#kraken-pro
/// </summary>
public const decimal MakerTier1CryptoFee = 0.0016m;
/// <summary>
/// We don't use 30 day model, so using only tier1 fees.
/// https://www.kraken.com/features/fee-schedule#kraken-pro
/// </summary>
public const decimal TakerTier1CryptoFee = 0.0026m;
/// <summary>
/// We don't use 30 day model, so using only tier1 fees.
/// https://www.kraken.com/features/fee-schedule#stablecoin-fx-pairs
/// </summary>
public const decimal Tier1FxFee = 0.002m;
/// <summary>
/// Fiats and stablecoins list that have own fee.
/// </summary>
public List<string> FxStablecoinList { get; init; } = new() {"CAD", "EUR", "GBP", "JPY", "USD", "USDT", "DAI", "USDC"};
/// <summary>
/// Get the fee for this order.
/// If sell - fees in base currency
/// If buy - fees in quote currency
/// It can be defined manually in <see cref="KrakenOrderProperties"/>
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The fee of the order</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
var isBuy = order.Direction == OrderDirection.Buy;
var unitPrice = isBuy ? security.AskPrice : security.BidPrice;
if (order.Type == OrderType.Limit)
{
// limit order posted to the order book
unitPrice = ((LimitOrder)order).LimitPrice;
}
unitPrice *= security.SymbolProperties.ContractMultiplier;
var fee = TakerTier1CryptoFee;
var props = order.Properties as KrakenOrderProperties;
if (order.Type == OrderType.Limit &&
(props?.PostOnly == true || !order.IsMarketable))
{
// limit order posted to the order book
fee = MakerTier1CryptoFee;
}
if (isBuy && props?.FeeInBase == true)
{
isBuy = false;
}
if (!isBuy && props?.FeeInQuote == true)
{
isBuy = true;
}
if (FxStablecoinList.Any(i => security.Symbol.Value.StartsWith(i)))
{
fee = Tier1FxFee;
}
string actualBaseCurrency;
string actualQuoteCurrency;
CurrencyPairUtil.DecomposeCurrencyPair(security.Symbol, out actualBaseCurrency, out actualQuoteCurrency);
return new OrderFee(new CashAmount(
isBuy ? unitPrice * order.AbsoluteQuantity * fee : 1 * order.AbsoluteQuantity * fee,
isBuy ? actualQuoteCurrency : actualBaseCurrency));
}
}
}
@@ -0,0 +1,56 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// An order fee where the fee quantity has already been subtracted from the filled quantity so instead we subtracted
/// from the quote currency when applied to the portfolio
/// </summary>
/// <remarks>
/// This type of order fee is returned by some crypto brokerages (e.g. Bitfinex and Binance)
/// with buy orders with cash accounts.
/// </remarks>
public class ModifiedFillQuantityOrderFee : OrderFee
{
private readonly string _quoteCurrency;
private readonly decimal _contractMultiplier;
/// <summary>
/// Initializes a new instance of the <see cref="ModifiedFillQuantityOrderFee"/> class
/// </summary>
/// <param name="orderFee">The order fee</param>
/// <param name="quoteCurrency">The associated security quote currency</param>
/// <param name="contractMultiplier">The associated security contract multiplier</param>
public ModifiedFillQuantityOrderFee(CashAmount orderFee, string quoteCurrency, decimal contractMultiplier)
: base(orderFee)
{
_quoteCurrency = quoteCurrency;
_contractMultiplier = contractMultiplier;
}
/// <summary>
/// Applies the order fee to the given portfolio
/// </summary>
/// <param name="portfolio">The portfolio instance</param>
/// <param name="fill">The order fill event</param>
public override void ApplyToPortfolio(SecurityPortfolioManager portfolio, OrderEvent fill)
{
portfolio.CashBook[_quoteCurrency].AddAmount(-Value.Amount * fill.FillPrice * _contractMultiplier);
}
}
}
+76
View File
@@ -0,0 +1,76 @@
/*
* 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 ProtoBuf;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Defines the result for <see cref="IFeeModel.GetOrderFee"/>
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class OrderFee
{
/// <summary>
/// Gets the order fee
/// </summary>
[ProtoMember(1)]
public CashAmount Value { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="OrderFee"/> class
/// </summary>
/// <param name="orderFee">The order fee</param>
public OrderFee(CashAmount orderFee)
{
Value = new CashAmount(
orderFee.Amount.Normalize(),
orderFee.Currency);
}
/// <summary>
/// Applies the order fee to the given portfolio
/// </summary>
/// <param name="portfolio">The portfolio instance</param>
/// <param name="fill">The order fill event</param>
public virtual void ApplyToPortfolio(SecurityPortfolioManager portfolio, OrderEvent fill)
{
portfolio.CashBook[Value.Currency].AddAmount(-Value.Amount);
}
/// <summary>
/// This is for backward compatibility with old 'decimal' order fee
/// </summary>
public override string ToString()
{
return $"{Value.Amount} {Value.Currency}";
}
/// <summary>
/// This is for backward compatibility with old 'decimal' order fee
/// </summary>
public static implicit operator decimal(OrderFee m)
{
return m.Value.Amount;
}
/// <summary>
/// Gets an instance of <see cref="OrderFee"/> that represents zero.
/// </summary>
public static readonly OrderFee Zero =
new OrderFee(new CashAmount(0, Currencies.NullCurrency));
}
}
+44
View File
@@ -0,0 +1,44 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Defines the parameters for <see cref="IFeeModel.GetOrderFee"/>
/// </summary>
public class OrderFeeParameters
{
/// <summary>
/// Gets the security
/// </summary>
public Security Security { get; }
/// <summary>
/// Gets the order
/// </summary>
public Order Order { get; }
/// <summary>
/// Initializes a new instance of the <see cref="OrderFeeParameters"/> class
/// </summary>
/// <param name="security">The security</param>
/// <param name="order">The order</param>
public OrderFeeParameters(Security security, Order order)
{
Security = security;
Order = order;
}
}
}
+131
View File
@@ -0,0 +1,131 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a fee model specific to Public.com.
/// </summary>
/// <see href="https://public.com/disclosures/fee-schedule"/>
/// <remarks>
/// Equity trades are free during regular market hours and $2.99 per trade during extended hours.
/// Options on stocks and ETFs are free; index options cost $0.50 per contract.
/// Crypto trades carry a fee that depends on the order amount in USD.
/// The model uses the regular member tier and does not detect OTC stocks.
/// </remarks>
public class PublicFeeModel : FeeModel
{
/// <summary>
/// Flat per-trade fee for US-listed equity trades placed during extended market hours.
/// </summary>
private const decimal _extendedHoursEquityFee = 2.99m;
/// <summary>
/// Per-contract fee for index options (regular member tier).
/// </summary>
private const decimal _indexOptionContractFee = 0.50m;
/// <summary>
/// Crypto fee charged on orders above the flat-tier range: 1.25% of the order amount.
/// </summary>
private const decimal _cryptoPercentFee = 0.0125m;
/// <summary>
/// Gets the order fee for a given security and order.
/// </summary>
/// <param name="parameters">The parameters including the security and order details.</param>
/// <returns>A <see cref="OrderFee"/> in USD for the order.</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
decimal fee;
switch (security.Type)
{
case SecurityType.Equity:
fee = GetEquityFee(security, order);
break;
case SecurityType.IndexOption:
fee = order.AbsoluteQuantity * _indexOptionContractFee;
break;
case SecurityType.Crypto:
fee = GetCryptoFee(security, order);
break;
default:
// Options on stocks and ETFs are commission-free on Public.com.
return OrderFee.Zero;
}
return new OrderFee(new CashAmount(fee, Currencies.USD));
}
/// <summary>
/// Returns the equity fee: free during regular market hours, a flat fee during extended hours.
/// </summary>
/// <param name="security">The traded security.</param>
/// <param name="order">The order, whose time decides whether the trade is during regular hours.</param>
/// <returns>The equity fee in USD.</returns>
private static decimal GetEquityFee(Security security, Order order)
{
var localOrderTime = order.Time.ConvertFromUtc(security.Exchange.TimeZone);
var isRegularHours = security.Exchange.Hours.IsOpen(localOrderTime, extendedMarketHours: false);
return isRegularHours ? 0m : _extendedHoursEquityFee;
}
/// <summary>
/// Returns the crypto fee for the order, tiered by the order amount in USD.
/// </summary>
/// <param name="security">The traded security.</param>
/// <param name="order">The order being placed.</param>
/// <returns>The crypto fee in USD.</returns>
private static decimal GetCryptoFee(Security security, Order order)
{
var orderAmount = security.Price * security.SymbolProperties.ContractMultiplier * order.AbsoluteQuantity;
if (orderAmount <= 10m)
{
return 0.49m;
}
if (orderAmount <= 25m)
{
return 0.69m;
}
if (orderAmount <= 50m)
{
return 1.19m;
}
if (orderAmount <= 100m)
{
return 1.69m;
}
if (orderAmount <= 250m)
{
return 3.29m;
}
if (orderAmount <= 500m)
{
return 6.29m;
}
return orderAmount * _cryptoPercentFee;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models RBI order fees
/// </summary>
public class RBIFeeModel : FeeModel
{
private readonly decimal _feesPerShare;
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="feesPerShare">The fees per share to apply</param>
/// <remarks>Default value is $0.005 per share</remarks>
public RBIFeeModel(decimal? feesPerShare = null)
{
_feesPerShare = feesPerShare ?? 0.005m;
}
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public OrderFee GetOrderFee(OrderFeeParameters parameters)
{
return new OrderFee(new CashAmount(_feesPerShare * parameters.Order.AbsoluteQuantity, Currencies.USD));
}
}
}
+58
View File
@@ -0,0 +1,58 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides the default implementation of <see cref="IFeeModel"/> Refer to https://www.samco.in/technology/brokerage_calculator
/// </summary>
public class SamcoFeeModel : IndiaFeeModel
{
/// <summary>
/// Brokerage calculation Factor
/// </summary>
protected override decimal BrokerageMultiplier => 0.0002M;
/// <summary>
/// Maximum brokerage per order
/// </summary>
protected override decimal MaxBrokerage => 20;
/// <summary>
/// Securities Transaction Tax calculation Factor
/// </summary>
protected override decimal SecuritiesTransactionTaxTotalMultiplier => 0.00025M;
/// <summary>
/// Exchange Transaction Charge calculation Factor
/// </summary>
protected override decimal ExchangeTransactionChargeMultiplier => 0.0000345M;
/// <summary>
/// State Tax calculation Factor
/// </summary>
protected override decimal StateTaxMultiplier => 0.18M;
/// <summary>
/// Sebi Charges calculation Factor
/// </summary>
protected override decimal SebiChargesMultiplier => 0.000001M;
/// <summary>
/// Stamp Charges calculation Factor
/// </summary>
protected override decimal StampChargesMultiplier => 0.00003M;
}
}
@@ -0,0 +1,48 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models TDAmeritrade order fees
/// </summary>
public class TDAmeritradeFeeModel : FeeModel
{
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var symbolSecurityType = order.Symbol.SecurityType;
switch(symbolSecurityType)
{
case SecurityType.Equity:
return new OrderFee(new CashAmount(0m, Currencies.USD));
case SecurityType.Option:
return new OrderFee(new CashAmount(0.65m * order.AbsoluteQuantity, Currencies.USD));
};
throw new System.ArgumentException(Messages.TDAmeritradeFeeModel.UnsupportedSecurityType(symbolSecurityType));
}
}
}
+97
View File
@@ -0,0 +1,97 @@
/*
* 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.Brokerages;
using QuantConnect.Securities;
using System;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a fee model specific to Tastytrade.
/// </summary>
/// <see href="https://tastytrade.com/pricing/"/>
public class TastytradeFeeModel : FeeModel
{
/// <summary>
/// Represents the fee associated with equity options transactions (per contract).
/// </summary>
private const decimal _optionFeeOpen = 1m;
/// <summary>
/// The fee associated with futures transactions (per contract).
/// </summary>
private const decimal _futureFee = 1.25m;
/// <summary>
/// The fee associated with futures options transactions (per contract).
/// </summary>
private const decimal _futureOptionFeeOpen = 2.5m;
/// <summary>
/// Gets the order fee for a given security and order.
/// </summary>
/// <param name="parameters">The parameters including the security and order details.</param>
/// <returns>
/// A <see cref="OrderFee"/> instance representing the total fee for the order,
/// or <see cref="OrderFee.Zero"/> if no fee is applicable.
/// </returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var feeRate = default(decimal);
switch (parameters.Security.Type)
{
case SecurityType.Option:
case SecurityType.IndexOption:
feeRate = IsOpenPosition(parameters.Order.Direction, parameters.Security.Holdings.Quantity) ? _optionFeeOpen : 0m;
break;
case SecurityType.Future:
feeRate = _futureFee;
break;
case SecurityType.FutureOption:
feeRate = IsOpenPosition(parameters.Order.Direction, parameters.Security.Holdings.Quantity) ? _futureOptionFeeOpen : 0m;
break;
default:
break;
}
return new OrderFee(new CashAmount(parameters.Order.AbsoluteQuantity * feeRate, Currencies.USD));
}
/// <summary>
/// Determines whether the specified order represents the opening of a new position.
/// </summary>
/// <param name="orderDirection">The direction of the order (buy/sell).</param>
/// <param name="holdingsQuantity">The current holdings quantity for the security.</param>
/// <returns>
/// <c>true</c> if the order is intended to open a new position; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="NotSupportedException">
/// Thrown when the resolved <see cref="OrderPosition"/> is not recognized.
/// </exception>
private static bool IsOpenPosition(OrderDirection orderDirection, decimal holdingsQuantity)
{
var orderPosition = BrokerageExtensions.GetOrderPosition(orderDirection, holdingsQuantity);
return orderPosition switch
{
OrderPosition.BuyToClose or OrderPosition.SellToClose => false,
OrderPosition.BuyToOpen or OrderPosition.SellToOpen => true,
_ => throw new NotSupportedException($"{nameof(TastytradeFeeModel)}.{nameof(IsOpenPosition)}: Unsupported order position: {orderPosition}")
};
}
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
using System;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a fee model specific to TradeStation.
/// </summary>
/// <see href="https://www.tradestation.com/pricing"/>
/// <remarks>
/// It is $0 for domestic and $5 for international clients for normal equities trades up to 10,000 shares, then $0.005 per share after.
/// Options are $0.60 per contract, per side, and an extra $1 for index options
/// </remarks>
public class TradeStationFeeModel : FeeModel
{
/// <summary>
/// Represents the fee associated with equity options transactions (per contract).
/// </summary>
private const decimal _equityOptionFee = 0.6m;
/// <summary>
/// Represents the fee associated with index options transactions (per contract).
/// </summary>
private const decimal _indexOptionFee = 1m;
/// <summary>
/// Represents the fee associated with futures transactions (per contract, per side).
/// </summary>
private const decimal _futuresFee = 1.5m;
/// <summary>
/// Gets the commission per trade based on the residency status of the entity or person.
/// </summary>
private decimal CommissionPerTrade => USResident ? 0m : 5.0m;
/// <summary>
/// Gets or sets a value indicating whether the entity or person is a resident of the United States.
/// </summary>
/// <value>
/// <c>true</c> if the entity or person is a US resident; otherwise, <c>false</c>.
/// </value>
public bool USResident { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="TradeStationFeeModel"/> class.
/// </summary>
/// <param name="usResident">
/// A boolean value indicating whether the entity or person is a US resident.
/// Default is <c>true</c>.
/// </param>
public TradeStationFeeModel(bool usResident = true)
{
USResident = usResident;
}
/// <summary>
/// Calculates the order fee based on the security type and order parameters.
/// </summary>
/// <param name="parameters">The parameters for the order fee calculation, which include security and order details.</param>
/// <returns>
/// An <see cref="OrderFee"/> instance representing the calculated order fee.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="parameters"/> is <c>null</c>.
/// </exception>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters), "Order fee parameters cannot be null.");
}
switch (parameters.Security.Type)
{
case SecurityType.Option:
return new OrderFee(new CashAmount(CommissionPerTrade + parameters.Order.AbsoluteQuantity * _equityOptionFee, Currencies.USD));
case SecurityType.IndexOption:
return new OrderFee(new CashAmount(CommissionPerTrade + parameters.Order.AbsoluteQuantity * _indexOptionFee, Currencies.USD));
case SecurityType.Future:
return new OrderFee(new CashAmount(parameters.Order.AbsoluteQuantity * _futuresFee, Currencies.USD));
default:
return new OrderFee(new CashAmount(CommissionPerTrade, Currencies.USD));
}
}
}
}
+226
View File
@@ -0,0 +1,226 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Represents a fee model specific to Webull.
/// </summary>
/// <see href="https://www.webull.com/pricing"/>
/// <remarks>
/// Equity and standard options trades are commission-free on Webull.
/// Index options carry a flat $0.50 Webull contract fee plus a variable exchange proprietary fee
/// that depends on the underlying index symbol and the option's market price.
/// </remarks>
public class WebullFeeModel : FeeModel
{
/// <summary>
/// Webull contract fee applied to every index option contract, regardless of underlying.
/// </summary>
private const decimal _webullIndexOptionContractFee = 0.50m;
/// <summary>
/// Exchange proprietary fee for SPX options priced below $1.00.
/// </summary>
private const decimal _spxExchangeFeeBelow1 = 0.57m;
/// <summary>
/// Exchange proprietary fee for SPX options priced at or above $1.00.
/// </summary>
private const decimal _spxExchangeFeeAbove1 = 0.66m;
/// <summary>
/// Exchange proprietary fee for SPXW options priced below $1.00.
/// </summary>
private const decimal _spxwExchangeFeeBelow1 = 0.50m;
/// <summary>
/// Exchange proprietary fee for SPXW options priced at or above $1.00.
/// </summary>
private const decimal _spxwExchangeFeeAbove1 = 0.59m;
/// <summary>
/// VIX/VIXW exchange fee tier 1: option price at or below $0.10.
/// </summary>
private const decimal _vixExchangeFeeTier1 = 0.10m;
/// <summary>
/// VIX/VIXW exchange fee tier 2: option price between $0.11 and $0.99.
/// </summary>
private const decimal _vixExchangeFeeTier2 = 0.25m;
/// <summary>
/// VIX/VIXW exchange fee tier 3: option price between $1.00 and $1.99.
/// </summary>
private const decimal _vixExchangeFeeTier3 = 0.40m;
/// <summary>
/// VIX/VIXW exchange fee tier 4: option price at or above $2.00.
/// </summary>
private const decimal _vixExchangeFeeTier4 = 0.45m;
/// <summary>
/// XSP exchange fee for orders with fewer than 10 contracts.
/// </summary>
private const decimal _xspExchangeFeeSmall = 0.00m;
/// <summary>
/// XSP exchange fee for orders with 10 or more contracts.
/// </summary>
private const decimal _xspExchangeFeeLarge = 0.07m;
/// <summary>
/// DJX flat exchange proprietary fee per contract.
/// </summary>
private const decimal _djxExchangeFee = 0.18m;
/// <summary>
/// NDX/NDXP exchange fee for single-leg orders with premium below $25.
/// </summary>
private const decimal _ndxSingleLegFeeBelow25 = 0.50m;
/// <summary>
/// NDX/NDXP exchange fee for single-leg orders with premium at or above $25.
/// </summary>
private const decimal _ndxSingleLegFeeAbove25 = 0.75m;
/// <summary>
/// NDX/NDXP exchange fee for multi-leg orders with premium below $25.
/// </summary>
private const decimal _ndxMultiLegFeeBelow25 = 0.65m;
/// <summary>
/// NDX/NDXP exchange fee for multi-leg orders with premium at or above $25.
/// </summary>
private const decimal _ndxMultiLegFeeAbove25 = 0.90m;
/// <summary>
/// Gets the order fee for a given security and order.
/// </summary>
/// <param name="parameters">The parameters including the security and order details.</param>
/// <returns>
/// <see cref="OrderFee.Zero"/> for equity and standard options;
/// a per-contract fee for index options.
/// </returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
switch (parameters.Security.Type)
{
case SecurityType.IndexOption:
return new OrderFee(new CashAmount(GetIndexOptionFee(parameters), Currencies.USD));
default:
// Equity and Option are commission-free on Webull.
return OrderFee.Zero;
}
}
/// <summary>
/// Calculates the total per-contract fee for an index option order.
/// The total fee = (exchange proprietary fee + Webull contract fee) × quantity.
/// </summary>
/// <param name="parameters">Order fee parameters containing the security and order.</param>
/// <returns>Total fee amount in USD.</returns>
private static decimal GetIndexOptionFee(OrderFeeParameters parameters)
{
var order = parameters.Order;
var security = parameters.Security;
var quantity = order.AbsoluteQuantity;
var price = security.Price;
var underlying = security.Symbol.Underlying?.Value?.ToUpperInvariant() ?? string.Empty;
var isMultiLeg = order.Type == OrderType.ComboMarket
|| order.Type == OrderType.ComboLimit
|| order.Type == OrderType.ComboLegLimit;
var exchangeFee = GetIndexOptionExchangeFee(underlying, price, quantity, isMultiLeg);
return quantity * (exchangeFee + _webullIndexOptionContractFee);
}
/// <summary>
/// Returns the exchange proprietary fee per contract for an index option, based on
/// the underlying ticker, the option's current price, order quantity, and leg type.
/// </summary>
/// <param name="underlying">Uppercase underlying ticker (e.g. "SPX", "VIX").</param>
/// <param name="price">Current market price of the option.</param>
/// <param name="quantity">Absolute number of contracts in the order.</param>
/// <param name="isMultiLeg">True when the order is a combo/multi-leg order.</param>
/// <returns>Exchange fee per contract in USD.</returns>
private static decimal GetIndexOptionExchangeFee(string underlying, decimal price, decimal quantity, bool isMultiLeg)
{
switch (underlying)
{
case "SPX":
return price < 1m ? _spxExchangeFeeBelow1 : _spxExchangeFeeAbove1;
case "SPXW":
return price < 1m ? _spxwExchangeFeeBelow1 : _spxwExchangeFeeAbove1;
case "VIX":
case "VIXW":
return GetVixExchangeFee(price);
case "XSP":
return quantity < 10m ? _xspExchangeFeeSmall : _xspExchangeFeeLarge;
case "DJX":
return _djxExchangeFee;
case "NDX":
case "NDXP":
return GetNdxExchangeFee(price, isMultiLeg);
default:
return 0m;
}
}
/// <summary>
/// Returns the VIX/VIXW exchange fee for a simple order based on the option price tier.
/// </summary>
private static decimal GetVixExchangeFee(decimal price)
{
if (price <= 0.10m)
{
return _vixExchangeFeeTier1;
}
if (price <= 0.99m)
{
return _vixExchangeFeeTier2;
}
if (price <= 1.99m)
{
return _vixExchangeFeeTier3;
}
return _vixExchangeFeeTier4;
}
/// <summary>
/// Returns the NDX/NDXP exchange fee per contract based on premium tier and order leg type.
/// </summary>
private static decimal GetNdxExchangeFee(decimal price, bool isMultiLeg)
{
if (isMultiLeg)
{
return price < 25m ? _ndxMultiLegFeeBelow25 : _ndxMultiLegFeeAbove25;
}
return price < 25m ? _ndxSingleLegFeeBelow25 : _ndxSingleLegFeeAbove25;
}
}
}
+50
View File
@@ -0,0 +1,50 @@
/*
* 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;
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides an implementation of <see cref="FeeModel"/> that models Wolverine order fees
/// </summary>
public class WolverineFeeModel : FeeModel
{
private readonly decimal _feesPerShare;
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="feesPerShare">The fees per share to apply</param>
/// <remarks>Default value is $0.005 per share</remarks>
public WolverineFeeModel(decimal? feesPerShare = null)
{
_feesPerShare = feesPerShare ?? 0.005m;
}
/// <summary>
/// Get the fee for this order in quote currency
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in quote currency</returns>
public OrderFee GetOrderFee(OrderFeeParameters parameters)
{
return new OrderFee(new CashAmount(_feesPerShare * parameters.Order.AbsoluteQuantity, Currencies.USD));
}
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fees
{
/// <summary>
/// Provides the default implementation of <see cref="IFeeModel"/> Refer to https://www.samco.in/technology/brokerage_calculator
/// </summary>
public class ZerodhaFeeModel : IndiaFeeModel
{
/// <summary>
/// Brokerage calculation Factor
/// </summary>
protected override decimal BrokerageMultiplier => 0.0003M;
/// <summary>
/// Maximum brokerage per order
/// </summary>
protected override decimal MaxBrokerage => 20;
/// <summary>
/// Securities Transaction Tax calculation Factor
/// </summary>
protected override decimal SecuritiesTransactionTaxTotalMultiplier => 0.00025M;
/// <summary>
/// Exchange Transaction Charge calculation Factor
/// </summary>
protected override decimal ExchangeTransactionChargeMultiplier => 0.0000345M;
/// <summary>
/// State Tax calculation Factor
/// </summary>
protected override decimal StateTaxMultiplier => 0.18M;
/// <summary>
/// Sebi Charges calculation Factor
/// </summary>
protected override decimal SebiChargesMultiplier => 0.000001M;
/// <summary>
/// Stamp Charges calculation Factor
/// </summary>
protected override decimal StampChargesMultiplier => 0.00003M;
/// <summary>
/// Checks if Stamp Charges is calculated from order valur or turnover
/// </summary>
protected override bool IsStampChargesFromOrderValue => true;
}
}
+92
View File
@@ -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 QuantConnect.Securities;
namespace QuantConnect.Orders.Fees;
/// <summary>
/// dYdX fee model implementation
/// </summary>
public class dYdXFeeModel : FeeModel
{
/// <summary>
/// Tier 1 maker fees
/// https://dydx.trade/portfolio/fees
/// </summary>
private const decimal MakerTier1Fee = 0.0001m;
/// <summary>
/// Tier 1 taker fees
/// https://dydx.trade/portfolio/fees
/// </summary>
private const decimal TakerTier1Fee = 0.0005m;
private readonly decimal _makerFee;
private readonly decimal _takerFee;
/// <summary>
/// Creates Binance fee model setting fees values
/// </summary>
/// <param name="mFee">Maker fee value</param>
/// <param name="tFee">Taker fee value</param>
public dYdXFeeModel(decimal mFee = MakerTier1Fee, decimal tFee = TakerTier1Fee)
{
_makerFee = mFee;
_takerFee = tFee;
}
/// <summary>
/// Gets the order fee associated with the specified order.
/// </summary>
/// <param name="parameters">A <see cref="OrderFeeParameters"/> object
/// containing the security and order</param>
/// <returns>The cost of the order in a <see cref="CashAmount"/> instance</returns>
public override OrderFee GetOrderFee(OrderFeeParameters parameters)
{
var security = parameters.Security;
var order = parameters.Order;
var fee = GetFee(order);
var positionValue = security.Holdings.GetQuantityValue(order.AbsoluteQuantity, security.Price);
return new OrderFee(new CashAmount(positionValue.Amount * fee, positionValue.Cash.Symbol));
}
/// <summary>
/// Gets the fee factor for the given order
/// </summary>
/// <param name="order">The order to get the fee factor for</param>
/// <returns>The fee factor for the given order</returns>
protected virtual decimal GetFee(Order order)
{
return GetFee(order, _makerFee, _takerFee);
}
private static decimal GetFee(Order order, decimal makerFee, decimal takerFee)
{
// apply fee factor, currently we do not model 30-day volume, so we use the first tier
var fee = takerFee;
var props = order.Properties as dYdXOrderProperties;
if (order.Type == OrderType.Limit && (props is { PostOnly: true } || !order.IsMarketable))
{
// limit order posted to the order book
fee = makerFee;
}
return fee;
}
}
File diff suppressed because it is too large Load Diff
+59
View File
@@ -0,0 +1,59 @@
/*
* 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;
using System.Collections.Generic;
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// Defines a possible result for <see cref="IFillModel.Fill"/> for a single order
/// </summary>
public class Fill : IEnumerable<OrderEvent>
{
private readonly List<OrderEvent> _orderEvents;
/// <summary>
/// Creates a new <see cref="Fill"/> instance
/// </summary>
/// <param name="orderEvents">The fill order events</param>
public Fill(List<OrderEvent> orderEvents)
{
_orderEvents = orderEvents;
}
/// <summary>
/// Creates a new <see cref="Fill"/> instance
/// </summary>
/// <param name="orderEvent">The fill order event</param>
public Fill(OrderEvent orderEvent)
{
_orderEvents = new() { orderEvent };
}
/// <summary>
/// Returns the order events enumerator
/// </summary>
public IEnumerator<OrderEvent> GetEnumerator()
{
return _orderEvents.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
/*
* 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;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// Defines the parameters for the <see cref="IFillModel"/> method
/// </summary>
public class FillModelParameters
{
/// <summary>
/// Gets the <see cref="Security"/>
/// </summary>
public Security Security { get; }
/// <summary>
/// Gets the <see cref="Order"/>
/// </summary>
public Order Order { get; }
/// <summary>
/// Gets the <see cref="SubscriptionDataConfig"/> provider
/// </summary>
public ISubscriptionDataConfigProvider ConfigProvider { get; }
/// <summary>
/// Gets the minimum time span elapsed to consider a market fill price as stale (defaults to one hour)
/// </summary>
public TimeSpan StalePriceTimeSpan { get; }
/// <summary>
/// Gets the collection of securities by order
/// </summary>
/// <remarks>We need this so that combo limit orders can access the prices for each security to calculate the price for the fill</remarks>
public Dictionary<Order, Security> SecuritiesForOrders { get; }
/// <summary>
/// Callback to notify when an order is updated by the fill model
/// </summary>
public Action<Order> OnOrderUpdated { get; }
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="security">Security asset we're filling</param>
/// <param name="order">Order packet to model</param>
/// <param name="configProvider">The <see cref="ISubscriptionDataConfigProvider"/> to use</param>
/// <param name="stalePriceTimeSpan">The minimum time span elapsed to consider a fill price as stale</param>
/// <param name="securitiesForOrders">Collection of securities for each order</param>
public FillModelParameters(
Security security,
Order order,
ISubscriptionDataConfigProvider configProvider,
TimeSpan stalePriceTimeSpan,
Dictionary<Order, Security> securitiesForOrders,
Action<Order> onOrderUpdated = null)
{
Security = security;
Order = order;
ConfigProvider = configProvider;
StalePriceTimeSpan = stalePriceTimeSpan;
SecuritiesForOrders = securitiesForOrders;
OnOrderUpdated = onOrderUpdated ?? (o => { });
}
}
}
+170
View File
@@ -0,0 +1,170 @@
/*
* 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.Util;
using QuantConnect.Data;
using QuantConnect.Securities;
using QuantConnect.Orders.Fees;
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// Represents the fill model used to simulate order fills for futures
/// </summary>
public class FutureFillModel : ImmediateFillModel
{
/// <summary>
/// Default market fill model for the base security class. Fills at the last traded price.
/// </summary>
/// <param name="asset">Security asset we're filling</param>
/// <param name="order">Order packet to model</param>
/// <returns>Order fill information detailing the average price and quantity filled.</returns>
public override OrderEvent MarketFill(Security asset, MarketOrder order)
{
//Default order event to return.
var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone);
var fill = new OrderEvent(order, utcTime, OrderFee.Zero);
if (order.Status == OrderStatus.Canceled) return fill;
// make sure the exchange is open on regular/extended market hours before filling
if (!IsExchangeOpen(asset, true)) return fill;
var prices = GetPricesCheckingPythonWrapper(asset, order.Direction);
var pricesEndTimeUtc = prices.EndTime.ConvertToUtc(asset.Exchange.TimeZone);
// If the order would be filled on stale (fill-forward / already past) data, wait for fresh data instead of
// filling at a stale price when the latest data is more than one resolution bar behind the order submission
// time (e.g. the first bar of the session after the open, or an intraday data gap). Otherwise fill on the
// stale price with a warning.
// Fetched lazily and reused across the stale-data check and the fill-price resolution so the subscription
// configs are resolved at most once per fill.
List<SubscriptionDataConfig> subscriptionConfigs = null;
if (pricesEndTimeUtc.Add(Parameters.StalePriceTimeSpan) < order.Time)
{
subscriptionConfigs = GetSubscriptionDataConfigs(asset);
if (ShouldWaitForFreshDataOnStale(asset, pricesEndTimeUtc, order.Time, subscriptionConfigs))
{
return fill;
}
fill.Message = Messages.FillModel.FilledAtStalePrice(asset, prices);
}
//Order [fill]price for a market order model is the current security price (or the bar open if the order
//was resting before this bar opened, see GetMarketFillPrice)
fill.FillPrice = GetMarketFillPrice(asset, order, prices, subscriptionConfigs);
fill.Status = OrderStatus.Filled;
//Calculate the model slippage: e.g. 0.01c
var slip = asset.SlippageModel.GetSlippageApproximation(asset, order);
//Apply slippage
switch (order.Direction)
{
case OrderDirection.Buy:
fill.FillPrice += slip;
break;
case OrderDirection.Sell:
fill.FillPrice -= slip;
break;
}
// assume the order completely filled
fill.FillQuantity = order.Quantity;
return fill;
}
/// <summary>
/// Stop fill model implementation for Future.
/// </summary>
/// <param name="asset">Security asset we're filling</param>
/// <param name="order">Order packet to model</param>
/// <returns>Order fill information detailing the average price and quantity filled.</returns>
/// <remarks>
/// A Stop order is an instruction to submit a buy or sell market order
/// if and when the user-specified stop trigger price is attained or penetrated.
///
/// A Sell Stop order is always placed below the current market price.
/// We assume a fluid/continuous, high volume market. Therefore, it is filled at the stop trigger price
/// if the current low price of trades is less than or equal to this price.
///
/// A Buy Stop order is always placed above the current market price.
/// We assume a fluid, high volume market. Therefore, it is filled at the stop trigger price
/// if the current high price of trades is greater or equal than this price.
///
/// The continuous market assumption is not valid if the market opens with an unfavorable gap.
/// In this case, a new bar opens below/above the stop trigger price, and the order is filled with the opening price.
/// <seealso cref="MarketFill(Security, MarketOrder)"/></remarks>
public override OrderEvent StopMarketFill(Security asset, StopMarketOrder order)
{
//Default order event to return.
var utcTime = asset.LocalTime.ConvertToUtc(asset.Exchange.TimeZone);
var fill = new OrderEvent(order, utcTime, OrderFee.Zero);
//If its cancelled don't need anymore checks:
if (order.Status == OrderStatus.Canceled) return fill;
// Fill only if open or extended
if (!IsExchangeOpen(asset, GetSubscriptionDataConfigs(asset).IsExtendedMarketHours()))
{
return fill;
}
//Get the range of prices in the last bar:
var prices = GetPricesCheckingPythonWrapper(asset, order.Direction);
var pricesEndTime = prices.EndTime.ConvertToUtc(asset.Exchange.TimeZone);
// do not fill on stale data
if (pricesEndTime <= order.Time) return fill;
//Calculate the model slippage: e.g. 0.01c
var slip = asset.SlippageModel.GetSlippageApproximation(asset, order);
//Check if the Stop Order was filled: opposite to a limit order
switch (order.Direction)
{
case OrderDirection.Sell:
//-> 1.1 Sell Stop: If Price below setpoint, Sell:
if (prices.Low < order.StopPrice)
{
fill.Status = OrderStatus.Filled;
// Assuming worse case scenario fill - fill at lowest of the stop & asset price.
fill.FillPrice = Math.Min(order.StopPrice, prices.Current - slip);
// assume the order completely filled
fill.FillQuantity = order.Quantity;
}
break;
case OrderDirection.Buy:
//-> 1.2 Buy Stop: If Price Above Setpoint, Buy:
if (prices.High > order.StopPrice)
{
fill.Status = OrderStatus.Filled;
// Assuming worse case scenario fill - fill at highest of the stop & asset price.
fill.FillPrice = Math.Max(order.StopPrice, prices.Current + slip);
// assume the order completely filled
fill.FillQuantity = order.Quantity;
}
break;
}
return fill;
}
}
}
@@ -0,0 +1,24 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// Represents the default fill model used to simulate order fills for future options
/// </summary>
public class FutureOptionFillModel : FutureFillModel
{
}
}
+32
View File
@@ -0,0 +1,32 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// Represents a model that simulates order fill events
/// </summary>
/// <remarks>Please use<see cref="FillModel"/> as the base class for
/// any implementations of<see cref="IFillModel"/></remarks>
public interface IFillModel
{
/// <summary>
/// Return an order event with the fill details
/// </summary>
/// <param name="parameters">A <see cref="FillModelParameters"/> object containing the security and order</param>
/// <returns>Order fill information detailing the average price and quantity filled.</returns>
Fill Fill(FillModelParameters parameters);
}
}
+24
View File
@@ -0,0 +1,24 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// Represents the default fill model used to simulate order fills
/// </summary>
public class ImmediateFillModel : FillModel
{
}
}
+101
View File
@@ -0,0 +1,101 @@
/*
* 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.Data.Market;
using QuantConnect.Securities;
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// This fill model is provided for cases where the trade/quote distinction should be
/// ignored and the fill price should be determined from the latest pricing information.
/// </summary>
public class LatestPriceFillModel : ImmediateFillModel
{
/// <summary>
/// Get the minimum and maximum price for this security in the last bar
/// Ignore the Trade/Quote distinction - fill with the latest pricing information
/// </summary>
/// <param name="asset">Security asset we're checking</param>
/// <param name="direction">The order direction, decides whether to pick bid or ask</param>
protected override Prices GetPrices(Security asset, OrderDirection direction)
{
var low = asset.Low;
var high = asset.High;
var open = asset.Open;
var close = asset.Close;
var current = asset.Price;
var lastData = asset.Cache.GetData();
var startTime = lastData?.Time ?? DateTime.MinValue;
var endTime = lastData?.EndTime ?? DateTime.MinValue;
if (direction == OrderDirection.Hold)
{
return new Prices(startTime, endTime, current, open, high, low, close);
}
// Only fill with data types we are subscribed to
var subscriptionTypes = Parameters.ConfigProvider
.GetSubscriptionDataConfigs(asset.Symbol)
.Select(x => x.Type).ToList();
// Tick
var tick = asset.Cache.GetData<Tick>();
if (subscriptionTypes.Contains(typeof(Tick)) && tick != null)
{
var price = direction == OrderDirection.Sell ? tick.BidPrice : tick.AskPrice;
if (price != 0m)
{
return new Prices(tick.Time, tick.EndTime, price, 0, 0, 0, 0);
}
// If the ask/bid spreads are not available for ticks, try the price
price = tick.Price;
if (price != 0m)
{
return new Prices(tick.Time, tick.EndTime, price, 0, 0, 0, 0);
}
}
// Get both the last trade and last quote
// Assume that the security has both a trade and quote subscription
// This should be true for crypto securities
var quoteBar = asset.Cache.GetData<QuoteBar>();
if (quoteBar != null)
{
var tradeBar = asset.Cache.GetData<TradeBar>();
if (tradeBar != null && tradeBar.EndTime > quoteBar.EndTime)
{
// The latest pricing data came from a trade
return new Prices(tradeBar);
}
else
{
// The latest pricing data came from a quote
var bar = direction == OrderDirection.Sell ? quoteBar.Bid : quoteBar.Ask;
if (bar != null)
{
return new Prices(quoteBar.Time, quoteBar.EndTime, bar);
}
}
}
return new Prices(startTime, endTime, current, open, high, low, close);
}
}
}
+102
View File
@@ -0,0 +1,102 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using QuantConnect.Data.Market;
namespace QuantConnect.Orders.Fills
{
/// <summary>
/// Prices class used by <see cref="IFillModel"/>s
/// </summary>
public class Prices
{
/// <summary>
/// Start time for these prices (when the bar opened)
/// </summary>
public DateTime Time { get; init; }
/// <summary>
/// End time for these prices
/// </summary>
public DateTime EndTime { get; init; }
/// <summary>
/// Current price
/// </summary>
public decimal Current { get; init; }
/// <summary>
/// Open price
/// </summary>
public decimal Open { get; init; }
/// <summary>
/// High price
/// </summary>
public decimal High { get; init; }
/// <summary>
/// Low price
/// </summary>
public decimal Low { get; init; }
/// <summary>
/// Closing price
/// </summary>
public decimal Close { get; init; }
/// <summary>
/// Create an instance of Prices class with a data bar
/// </summary>
/// <param name="bar">Data bar to use for prices</param>
public Prices(IBaseDataBar bar)
: this(bar.Time, bar.EndTime, bar.Close, bar.Open, bar.High, bar.Low, bar.Close)
{
}
/// <summary>
/// Create an instance of Prices class with a data bar and start/end time
/// </summary>
/// <param name="time">The start time for these prices (when the bar opened)</param>
/// <param name="endTime">The end time for these prices</param>
/// <param name="bar">Data bar to use for prices</param>
public Prices(DateTime time, DateTime endTime, IBar bar)
: this(time, endTime, bar.Close, bar.Open, bar.High, bar.Low, bar.Close)
{
}
/// <summary>
/// Create a instance of the Prices class with specific values for all prices
/// </summary>
/// <param name="time">The start time for these prices (when the bar opened)</param>
/// <param name="endTime">The end time for these prices</param>
/// <param name="current">Current price</param>
/// <param name="open">Open price</param>
/// <param name="high">High price</param>
/// <param name="low">Low price</param>
/// <param name="close">Close price</param>
public Prices(DateTime time, DateTime endTime, decimal current, decimal open, decimal high, decimal low, decimal close)
{
Time = time;
EndTime = endTime;
Current = current;
Open = open == 0 ? current : open;
High = high == 0 ? current : high;
Low = low == 0 ? current : low;
Close = close == 0 ? current : close;
}
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// FIX (Financial Information Exchange) order properties
/// </summary>
public class FixOrderProperites: OrderProperties
{
/// <summary>
/// Instruction for order handling on Broker floor
/// </summary>
public char? HandleInstruction { get; set; }
/// <summary>
/// Free format text string
/// </summary>
public string Notes { get; set; }
/// <summary>
/// Automated execution order, private, no broker intervention
/// </summary>
public const char AutomatedExecutionOrderPrivate = '1';
/// <summary>
/// Automated execution order, public, broker, intervention OK
/// </summary>
public const char AutomatedExecutionOrderPublic = '2';
/// <summary>
/// Staged order, broker intervention required
/// </summary>
public const char ManualOrder = '3';
}
}
+26
View File
@@ -0,0 +1,26 @@
/*
* 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.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to GDAX brokerage
/// </summary>
[Obsolete("GDAXOrderProperties is deprecated. Use CoinbaseOrderProperties instead.")]
public class GDAXOrderProperties : CoinbaseOrderProperties
{ }
}
+91
View File
@@ -0,0 +1,91 @@
/*
* 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.Collections.Concurrent;
namespace QuantConnect.Orders
{
/// <summary>
/// Provides a thread-safe service for caching and managing original orders when they are part of a group.
/// </summary>
public class GroupOrderCacheManager
{
/// <summary>
/// A thread-safe dictionary that caches original orders when they are part of a group.
/// </summary>
/// <remarks>
/// The dictionary uses the order ID as the key and stores the original <see cref="Order"/> objects as values.
/// This allows for the modification of the original orders, such as setting the brokerage ID,
/// without retrieving a cloned instance from the order provider.
/// </remarks>
private readonly ConcurrentDictionary<int, Order> _pendingGroupOrders = new();
/// <summary>
/// Attempts to retrieve all the orders in the combo group from the cache.
/// </summary>
/// <param name="order">Target order, which can be any of the legs of the combo</param>
/// <param name="orders">List of orders in the combo</param>
/// <returns>
/// <c>true</c> if all the orders in the combo group were successfully retrieved from the cache;
/// otherwise, <c>false</c>. If the retrieval fails, the target order is cached for future retrieval.
/// </returns>
public bool TryGetGroupCachedOrders(Order order, out List<Order> orders)
{
if (!order.TryGetGroupOrders(TryGetOrder, out orders))
{
// some order of the group is missing but cache the new one
CacheOrder(order);
return false;
}
RemoveCachedOrders(orders);
return true;
}
/// <summary>
/// Attempts to retrieve an original order from the cache using the specified order ID.
/// </summary>
/// <param name="orderId">The unique identifier of the order to retrieve.</param>
/// <returns>
/// The original <see cref="Order"/> if found; otherwise, <c>null</c>.
/// </returns>
private Order TryGetOrder(int orderId)
{
_pendingGroupOrders.TryGetValue(orderId, out var order);
return order;
}
/// <summary>
/// Caches an original order in the internal dictionary for future retrieval.
/// </summary>
/// <param name="order">The <see cref="Order"/> object to cache.</param>
private void CacheOrder(Order order)
{
_pendingGroupOrders[order.Id] = order;
}
/// <summary>
/// Removes a list of orders from the internal cache.
/// </summary>
/// <param name="orders">The list of <see cref="Order"/> objects to remove from the cache.</param>
private void RemoveCachedOrders(List<Order> orders)
{
for (var i = 0; i < orders.Count; i++)
{
_pendingGroupOrders.TryRemove(orders[i].Id, out _);
}
}
}
}
+160
View File
@@ -0,0 +1,160 @@
/*
* 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.Logging;
using QuantConnect.Securities;
using System.Collections.Generic;
namespace QuantConnect.Orders
{
/// <summary>
/// Group (combo) orders extension methods for easiest combo order manipulation
/// </summary>
public static class GroupOrderExtensions
{
/// <summary>
/// Gets the grouped orders (legs) of a group order
/// </summary>
/// <param name="order">Target order, which can be any of the legs of the combo</param>
/// <param name="orderProvider">Order provider to use to access the existing orders</param>
/// <param name="orders">List of orders in the combo</param>
/// <returns>False if any of the orders in the combo is not yet found in the order provider. True otherwise</returns>
/// <remarks>If the target order is not a combo order, the resulting list will contain that single order alone</remarks>
public static bool TryGetGroupOrders(this Order order, Func<int, Order> orderProvider, out List<Order> orders)
{
orders = new List<Order> { order };
if (order.GroupOrderManager != null)
{
lock (order.GroupOrderManager.OrderIds)
{
foreach (var otherOrdersId in order.GroupOrderManager.OrderIds.Where(id => id != order.Id))
{
var otherOrder = orderProvider(otherOrdersId);
if (otherOrder != null)
{
orders.Add(otherOrder);
}
else
{
// this will happen while all the orders haven't arrived yet, we will retry
return false;
}
}
}
if (order.GroupOrderManager.Count != orders.Count)
{
if (Log.DebuggingEnabled)
{
Log.Debug($"GroupOrderExtensions.TryGetGroupOrders(): missing orders of group {order.GroupOrderManager.Id}." +
$" We have {orders.Count}/{order.GroupOrderManager.Count} orders will skip");
}
return false;
}
}
orders.Sort((x, y) => x.Id.CompareTo(y.Id));
return true;
}
/// <summary>
/// Gets the securities corresponding to each order in the group
/// </summary>
/// <param name="orders">List of orders to map</param>
/// <param name="securityProvider">The security provider to use</param>
/// <param name="securities">The resulting map of order to security</param>
/// <returns>True if the mapping is successful, false otherwise.</returns>
public static bool TryGetGroupOrdersSecurities(this List<Order> orders, ISecurityProvider securityProvider, out Dictionary<Order, Security> securities)
{
securities = new(orders.Count);
for (var i = 0; i < orders.Count; i++)
{
var order = orders[i];
var security = securityProvider.GetSecurity(order.Symbol);
if (security == null)
{
return false;
}
securities[order] = security;
}
return true;
}
/// <summary>
/// Returns an error string message saying there is insufficient buying power for the given orders associated with their respective
/// securities
/// </summary>
public static string GetErrorMessage(this Dictionary<Order, Security> securities, HasSufficientBuyingPowerForOrderResult hasSufficientBuyingPowerResult)
{
return Messages.GroupOrderExtensions.InsufficientBuyingPowerForOrders(securities, hasSufficientBuyingPowerResult);
}
/// <summary>
/// Gets the combo order leg group quantity, that is, the total number of shares to be bought/sold from this leg,
/// from its ratio and the group order quantity
/// </summary>
/// <param name="legRatio">The leg ratio</param>
/// <param name="groupOrderManager">The group order manager</param>
/// <returns>The total number of shares to be bought/sold from this leg</returns>
public static decimal GetOrderLegGroupQuantity(this decimal legRatio, GroupOrderManager groupOrderManager)
{
return groupOrderManager != null ? legRatio * groupOrderManager.Quantity : legRatio;
}
/// <summary>
/// Gets the combo order leg ratio from its group quantity and the group order quantity
/// </summary>
/// <param name="legGroupQuantity">
/// The total number of shares to be bought/sold from this leg, that is, the result of the let ratio times the group quantity
/// </param>
/// <param name="groupOrderManager">The group order manager</param>
/// <returns>The ratio of this combo order leg</returns>
public static decimal GetOrderLegRatio(this decimal legGroupQuantity, GroupOrderManager groupOrderManager)
{
return groupOrderManager != null ? legGroupQuantity / groupOrderManager.Quantity : legGroupQuantity;
}
/// <summary>
/// Calculates the greatest common divisor (GCD) of the provided leg quantities
/// and returns it as a signed quantity based on the <see cref="OrderDirection"/>.
/// </summary>
/// <param name="legQuantity">A collection of leg quantities.</param>
/// <param name="orderDirection">
/// Determines the sign of the returned quantity:
/// <see cref="OrderDirection.Buy"/> returns a positive quantity,
/// <see cref="OrderDirection.Sell"/> returns a negative quantity.
/// </param>
/// <returns>
/// The greatest common divisor of the leg quantities, signed according to <paramref name="orderDirection"/>.
/// </returns>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="orderDirection"/> has an unsupported value.
/// </exception>
public static decimal GetGroupQuantityByEachLegQuantity(IEnumerable<decimal> legQuantity, OrderDirection orderDirection)
{
var groupQuantity = Extensions.GreatestCommonDivisor(legQuantity.Select(Math.Abs));
return orderDirection switch
{
OrderDirection.Buy => groupQuantity,
OrderDirection.Sell => decimal.Negate(groupQuantity),
_ => throw new ArgumentException($"Unsupported {nameof(OrderDirection)} value: '{orderDirection}'.", nameof(orderDirection))
};
}
}
}
+117
View File
@@ -0,0 +1,117 @@
/*
* 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 Newtonsoft.Json;
using System.Collections.Generic;
namespace QuantConnect.Orders
{
/// <summary>
/// Manager of a group of orders
/// </summary>
public class GroupOrderManager
{
/// <summary>
/// The unique order group Id
/// </summary>
[JsonProperty(PropertyName = "id")]
public int Id { get; internal set; }
/// <summary>
/// The group order quantity
/// </summary>
[JsonProperty(PropertyName = "quantity")]
public decimal Quantity { get; internal set; }
/// <summary>
/// The total order count associated with this order group
/// </summary>
[JsonProperty(PropertyName = "count")]
public int Count { get; }
/// <summary>
/// The limit price associated with this order group if any
/// </summary>
[JsonProperty(PropertyName = "limitPrice")]
public decimal LimitPrice { get; set; }
/// <summary>
/// The order Ids in this group
/// </summary>
/// <remarks>In live trading we process orders in a dedicated thread so we need to be thread safe</remarks>
[JsonProperty(PropertyName = "orderIds")]
public HashSet<int> OrderIds { get; }
/// <summary>
/// Order Direction Property based off Quantity.
/// </summary>
[JsonProperty(PropertyName = "direction")]
public OrderDirection Direction
{
get
{
if (Quantity > 0)
{
return OrderDirection.Buy;
}
if (Quantity < 0)
{
return OrderDirection.Sell;
}
return OrderDirection.Hold;
}
}
/// <summary>
/// Get the absolute quantity for this combo order
/// </summary>
[JsonIgnore]
public decimal AbsoluteQuantity => Math.Abs(Quantity);
/// <summary>
/// Creates a new empty instance
/// </summary>
public GroupOrderManager()
{
}
/// <summary>
/// Creates a new instance of <see cref="GroupOrderManager"/>
/// </summary>
/// <param name="id">This order group unique Id</param>
/// <param name="legCount">The order leg count</param>
/// <param name="quantity">The group order quantity</param>
/// <param name="limitPrice">The limit price associated with this order group if any</param>
public GroupOrderManager(int id, int legCount, decimal quantity, decimal limitPrice = 0) : this(legCount, quantity, limitPrice)
{
Id = id;
}
/// <summary>
/// Creates a new instance of <see cref="GroupOrderManager"/>
/// </summary>
/// <param name="legCount">The order leg count</param>
/// <param name="quantity">The group order quantity</param>
/// <param name="limitPrice">The limit price associated with this order group if any</param>
public GroupOrderManager(int legCount, decimal quantity, decimal limitPrice = 0)
{
Count = legCount;
Quantity = quantity;
LimitPrice = limitPrice;
OrderIds = new(capacity: legCount);
}
}
}
+77
View File
@@ -0,0 +1,77 @@
/*
* 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.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to Indian Brokerages
/// </summary>
public class IndiaOrderProperties : OrderProperties
{
/// <summary>
/// India product type
/// </summary>
public string ProductType { get; }
/// <summary>
/// Define the India Order type that we are targeting (MIS/CNC/NRML).
/// </summary>
public enum IndiaProductType
{
/// <summary>
/// Margin Intraday Square Off (0)
/// </summary>
MIS,
/// <summary>
/// Cash and Carry (1)
/// </summary>
CNC,
/// <summary>
/// Normal (2)
/// </summary>
NRML
}
/// <summary>
/// Initialize a new OrderProperties for <see cref="IndiaOrderProperties"/>
/// </summary>
/// <param name="exchange">Exchange value, nse/bse etc</param>
public IndiaOrderProperties(Exchange exchange) : base(exchange)
{
}
/// <summary>
/// Initialize a new OrderProperties for <see cref="IndiaOrderProperties"/>
/// </summary>
/// <param name="exchange">Exchange value, nse/bse etc</param>
/// <param name="productType">ProductType value, MIS/CNC/NRML etc</param>
public IndiaOrderProperties(Exchange exchange, IndiaProductType productType) : base(exchange)
{
ProductType = productType.ToString();
}
/// <summary>
/// Returns a new instance clone of this object
/// </summary>
public override IOrderProperties Clone()
{
return (IndiaOrderProperties)MemberwiseClone();
}
}
}
@@ -0,0 +1,28 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to Fix Interactive Brokers
/// </summary>
public class InteractiveBrokersFixOrderProperties : OrderProperties
{
/// <summary>
/// The linked account for which to submit the order (only used by Financial Advisors)
/// </summary>
public string Account { get; set; }
}
}
@@ -0,0 +1,67 @@
/*
* 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.Orders
{
/// <summary>
/// Contains additional properties and settings for an order submitted to Interactive Brokers
/// </summary>
public class InteractiveBrokersOrderProperties : OrderProperties
{
/// <summary>
/// The linked account for which to submit the order (only used by Financial Advisors)
/// </summary>
/// <remarks>Mutually exclusive with FaProfile and FaGroup</remarks>
public string Account { get; set; }
/// <summary>
/// The account group for the order (only used by Financial Advisors)
/// </summary>
/// <remarks>Mutually exclusive with FaProfile and Account</remarks>
public string FaGroup { get; set; }
/// <summary>
/// The allocation method for the account group order (only used by Financial Advisors)
/// Supported allocation methods are: Equal, NetLiq, AvailableEquity, PctChange
/// </summary>
public string FaMethod { get; set; }
/// <summary>
/// The percentage for the percent change method (only used by Financial Advisors)
/// </summary>
public int FaPercentage { get; set; }
/// <summary>
/// The allocation profile to be used for the order (only used by Financial Advisors)
/// </summary>
/// <remarks>Mutually exclusive with FaGroup and Account</remarks>
public string FaProfile { get; set; }
/// <summary>
/// If set to true, allows orders to also trigger or fill outside of regular trading hours.
/// </summary>
public bool OutsideRegularTradingHours { get; set; }
/// <summary>
/// Returns a new instance clone of this object
/// </summary>
public override IOrderProperties Clone()
{
return (InteractiveBrokersOrderProperties)MemberwiseClone();
}
}
}
+82
View File
@@ -0,0 +1,82 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Kraken order properties
/// </summary>
public class KrakenOrderProperties : OrderProperties
{
private bool _feeInQuote;
private bool _feeInBase;
/// <summary>
/// Post-only order (available when ordertype = limit)
/// </summary>
public bool PostOnly { get; set; }
/// <summary>
/// If true or by default when selling, fees will be charged in base currency. If false will be ignored. Mutually exclusive with FeeInQuote.
/// </summary>
/// <remarks>See (https://support.kraken.com/hc/en-us/articles/202966956#howclosingtransactionswork) for more information about the currency selection.</remarks>
public bool FeeInBase
{
get
{
return _feeInBase;
}
set
{
if (value)
{
_feeInBase = value;
_feeInQuote = !_feeInBase;
}
}
}
/// <summary>
/// If true or by default when buying, fees will be charged in quote currency. If false will be ignored. Mutually exclusive with FeeInBase.
/// </summary>
/// <remarks>See (https://support.kraken.com/hc/en-us/articles/202966956#howclosingtransactionswork) for more information about the currency selection.</remarks>
public bool FeeInQuote
{
get
{
return _feeInQuote;
}
set
{
if (value)
{
_feeInQuote = value;
_feeInBase = !_feeInQuote;
}
}
}
/// <summary>
/// https://support.kraken.com/hc/en-us/articles/201648183-Market-Price-Protection
/// </summary>
public bool NoMarketPriceProtection { get; set; }
/// <summary>
/// Conditional close orders are triggered by execution of the primary order in the same quantity and opposite direction. Ordertypes can be the same with primary order.
/// </summary>
public Order ConditionalOrder { get; set; }
}
}
+49
View File
@@ -0,0 +1,49 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Basic order leg
/// </summary>
public class Leg
{
/// <summary>
/// The legs symbol
/// </summary>
public Symbol Symbol { get; set; }
/// <summary>
/// Quantity multiplier used to specify proper scale (and direction) of the leg within the strategy
/// </summary>
public int Quantity { get; set; }
/// <summary>
/// Order limit price of the leg in case limit order is sent to the market on strategy execution
/// </summary>
public decimal? OrderPrice { get; set; }
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="quantity">The quantity</param>
/// <param name="limitPrice">Associated limit price if any</param>
public static Leg Create(Symbol symbol, int quantity, decimal? limitPrice = null)
{
return new Leg { Symbol = symbol, Quantity = quantity, OrderPrice= limitPrice};
}
}
}
+160
View File
@@ -0,0 +1,160 @@
/*
* 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 Newtonsoft.Json;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// In effect, a LimitIfTouchedOrder behaves opposite to the <see cref="StopLimitOrder"/>;
/// after a trigger price is touched, a limit order is set for some user-defined value above (below)
/// the trigger when selling (buying).
/// https://www.interactivebrokers.ca/en/index.php?f=45318
/// </summary>
public class LimitIfTouchedOrder : Order
{
/// <summary>
/// Order Type
/// </summary>
public override OrderType Type => OrderType.LimitIfTouched;
/// <summary>
/// The price which, when touched, will trigger the setting of a limit order at <see cref="LimitPrice"/>.
/// </summary>
[JsonProperty(PropertyName = "triggerPrice")]
public decimal TriggerPrice { get; internal set; }
/// <summary>
/// The price at which to set the limit order following <see cref="TriggerPrice"/> being touched.
/// </summary>
[JsonProperty(PropertyName = "limitPrice")]
public decimal LimitPrice { get; internal set; }
/// <summary>
/// Whether or not the <see cref="TriggerPrice"/> has been touched.
/// </summary>
[JsonProperty(PropertyName = "triggerTouched")]
public bool TriggerTouched { get; internal set; }
/// <summary>
/// New <see cref="LimitIfTouchedOrder"/> constructor.
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="limitPrice">Maximum price to fill the order</param>
/// <param name="time">Time the order was placed</param>
/// <param name="triggerPrice">Price which must be touched in order to then set a limit order</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public LimitIfTouchedOrder(
Symbol symbol,
decimal quantity,
decimal? triggerPrice,
decimal limitPrice,
DateTime time,
string tag = "",
IOrderProperties properties = null
)
: base(symbol, quantity, time, tag, properties)
{
TriggerPrice = (decimal) triggerPrice;
LimitPrice = limitPrice;
}
/// <summary>
/// Default constructor for JSON Deserialization:
/// </summary>
public LimitIfTouchedOrder()
{
}
/// <summary>
/// Gets the default tag for this order
/// </summary>
/// <returns>The default tag</returns>
public override string GetDefaultTag()
{
return Messages.LimitIfTouchedOrder.Tag(this);
}
/// <summary>
/// Modifies the state of this order to match the update request
/// </summary>
/// <param name="request">The request to update this order object</param>
public override void ApplyUpdateOrderRequest(UpdateOrderRequest request)
{
base.ApplyUpdateOrderRequest(request);
if (request.TriggerPrice.HasValue)
{
TriggerPrice = request.TriggerPrice.Value;
}
if (request.LimitPrice.HasValue)
{
LimitPrice = request.LimitPrice.Value;
}
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new LimitIfTouchedOrder
{TriggerPrice = TriggerPrice, LimitPrice = LimitPrice, TriggerTouched = TriggerTouched};
CopyTo(order);
return order;
}
/// <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.LimitIfTouchedOrder.ToString(this);
}
/// <summary>
/// Gets the order value in units of the security's quote currency for a single unit.
/// A single unit here is a single share of stock, or a single barrel of oil, or the
/// cost of a single share in an option contract.
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
// selling, so higher price will be used
if (Quantity < 0)
{
return Quantity * Math.Max(LimitPrice, security.Price);
}
// buying, so lower price will be used
if (Quantity > 0)
{
return Quantity * Math.Min(LimitPrice, security.Price);
}
return 0m;
}
}
}
+135
View File
@@ -0,0 +1,135 @@
/*
* 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 Newtonsoft.Json;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Limit order type definition
/// </summary>
public class LimitOrder : Order
{
/// <summary>
/// Limit price for this order.
/// </summary>
[JsonProperty(PropertyName = "limitPrice")]
public decimal LimitPrice { get; internal set; }
/// <summary>
/// Limit Order Type
/// </summary>
public override OrderType Type
{
get { return OrderType.Limit; }
}
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
public LimitOrder()
{
}
/// <summary>
/// New limit order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="limitPrice">Price the order should be filled at if a limit order</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public LimitOrder(Symbol symbol, decimal quantity, decimal limitPrice, DateTime time, string tag = "", IOrderProperties properties = null)
: base(symbol, quantity, time, tag, properties)
{
LimitPrice = limitPrice;
}
/// <summary>
/// Gets the default tag for this order
/// </summary>
/// <returns>The default tag</returns>
public override string GetDefaultTag()
{
return Messages.LimitOrder.Tag(this);
}
/// <summary>
/// Gets the order value in units of the security's quote currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
return CalculateOrderValue(Quantity, LimitPrice, security.Price);
}
/// <summary>
/// Modifies the state of this order to match the update request
/// </summary>
/// <param name="request">The request to update this order object</param>
public override void ApplyUpdateOrderRequest(UpdateOrderRequest request)
{
base.ApplyUpdateOrderRequest(request);
if (request.LimitPrice.HasValue)
{
LimitPrice = request.LimitPrice.Value;
}
}
/// <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.LimitOrder.ToString(this);
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new LimitOrder { LimitPrice = LimitPrice };
CopyTo(order);
return order;
}
internal static decimal CalculateOrderValue(decimal quantity, decimal limitPrice, decimal price)
{
// selling, so higher price will be used
if (quantity < 0)
{
return quantity * Math.Max(limitPrice, price);
}
// buying, so lower price will be used
if (quantity > 0)
{
return quantity * Math.Min(limitPrice, price);
}
return 0m;
}
}
}
+92
View File
@@ -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 QuantConnect.Interfaces;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Market on close order type - submits a market order on exchange close
/// </summary>
public class MarketOnCloseOrder : Order
{
/// <summary>
/// Gets the default interval before market close that an MOC order may be submitted.
/// For example, US equity exchanges typically require MOC orders to be placed no later
/// than 15 minutes before market close, which yields a nominal time of 3:45PM.
/// This buffer value takes into account the 15 minutes and adds an additional 30 seconds
/// to account for other potential delays, such as LEAN order processing and placement of
/// the order to the exchange.
/// </summary>
public static readonly TimeSpan DefaultSubmissionTimeBuffer = TimeSpan.FromMinutes(15.5);
/// <summary>
/// The interval before market close that an MOC order may be submitted.
/// </summary>
/// <remarks>Configurable so advanced users may modify this for special cases;
/// Related issue: Github #5481</remarks>
public static TimeSpan SubmissionTimeBuffer = DefaultSubmissionTimeBuffer;
/// <summary>
/// MarketOnClose Order Type
/// </summary>
public override OrderType Type
{
get { return OrderType.MarketOnClose; }
}
/// <summary>
/// Intiializes a new instance of the <see cref="MarketOnCloseOrder"/> class.
/// </summary>
public MarketOnCloseOrder()
{
}
/// <summary>
/// Intiializes a new instance of the <see cref="MarketOnCloseOrder"/> class.
/// </summary>
/// <param name="symbol">The security's symbol being ordered</param>
/// <param name="quantity">The number of units to order</param>
/// <param name="time">The current time</param>
/// <param name="tag">A user defined tag for the order</param>
/// <param name="properties">The order properties for this order</param>
public MarketOnCloseOrder(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null)
: base(symbol, quantity, time, tag, properties)
{
}
/// <summary>
/// Gets the order value in units of the security's quote currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
return Quantity*security.Price;
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new MarketOnCloseOrder();
CopyTo(order);
return order;
}
}
}
+75
View File
@@ -0,0 +1,75 @@
/*
* 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;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Market on Open order type, submits a market order when the exchange opens
/// </summary>
public class MarketOnOpenOrder : Order
{
/// <summary>
/// MarketOnOpen Order Type
/// </summary>
public override OrderType Type
{
get { return OrderType.MarketOnOpen; }
}
/// <summary>
/// Intiializes a new instance of the <see cref="MarketOnOpenOrder"/> class.
/// </summary>
public MarketOnOpenOrder()
{
}
/// <summary>
/// Intiializes a new instance of the <see cref="MarketOnOpenOrder"/> class.
/// </summary>
/// <param name="symbol">The security's symbol being ordered</param>
/// <param name="quantity">The number of units to order</param>
/// <param name="time">The current time</param>
/// <param name="tag">A user defined tag for the order</param>
/// <param name="properties">The order properties for this order</param>
public MarketOnOpenOrder(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null)
: base(symbol, quantity, time, tag, properties)
{
}
/// <summary>
/// Gets the order value in units of the security's quote currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
return Quantity*security.Price;
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new MarketOnOpenOrder();
CopyTo(order);
return order;
}
}
}
+90
View File
@@ -0,0 +1,90 @@
/*
* 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;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Market order type definition
/// </summary>
public class MarketOrder : Order
{
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
public MarketOrder()
{
}
/// <summary>
/// Market Order Type
/// </summary>
public override OrderType Type
{
get { return OrderType.Market; }
}
/// <summary>
/// New market order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="price">Price of the order</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public MarketOrder(Symbol symbol, decimal quantity, DateTime time, decimal price, string tag = "", IOrderProperties properties = null)
: this(symbol, quantity, time, tag, properties)
{
Price = price;
}
/// <summary>
/// New market order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public MarketOrder(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null)
: base(symbol, quantity, time, tag, properties)
{
}
/// <summary>
/// Gets the order value in units of the security's quote currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
return Quantity*security.Price;
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new MarketOrder();
CopyTo(order);
return order;
}
}
}
@@ -0,0 +1,77 @@
/*
* 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.Option;
using static QuantConnect.Extensions;
namespace QuantConnect.Orders.OptionExercise
{
/// <summary>
/// Represents the default option exercise model (physical, cash settlement)
/// </summary>
public class DefaultExerciseModel : IOptionExerciseModel
{
/// <summary>
/// Default option exercise model for the basic equity/index option security class.
/// </summary>
/// <param name="option">Option we're trading this order</param>
/// <param name="order">Order to update</param>
public virtual IEnumerable<OrderEvent> OptionExercise(Option option, OptionExerciseOrder order)
{
var underlying = option.Underlying;
var utcTime = option.LocalTime.ConvertToUtc(option.Exchange.TimeZone);
var inTheMoney = option.IsAutoExercised(underlying.Close);
var isAssignment = inTheMoney && option.Holdings.IsShort;
yield return new OrderEvent(
order.Id,
option.Symbol,
utcTime,
OrderStatus.Filled,
GetOrderDirection(order.Quantity),
0.0m,
order.Quantity,
OrderFee.Zero,
Messages.DefaultExerciseModel.ContractHoldingsAdjustmentFillTag(inTheMoney, isAssignment, option)
)
{
IsAssignment = isAssignment,
IsInTheMoney = inTheMoney
};
// TODO : Support Manual Exercise of OTM contracts [ inTheMoney = false ]
if (inTheMoney && option.ExerciseSettlement == SettlementType.PhysicalDelivery)
{
var exerciseQuantity = option.GetExerciseQuantity(order.Quantity);
yield return new OrderEvent(
order.Id,
underlying.Symbol,
utcTime,
OrderStatus.Filled,
GetOrderDirection(exerciseQuantity),
option.StrikePrice,
exerciseQuantity,
OrderFee.Zero,
isAssignment ? Messages.DefaultExerciseModel.OptionAssignment : Messages.DefaultExerciseModel.OptionExercise
) { IsInTheMoney = true };
}
}
}
}
@@ -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.
*/
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
using System.Collections.Generic;
namespace QuantConnect.Orders.OptionExercise
{
/// <summary>
/// Represents a model that simulates option exercise and lapse events
/// </summary>
public interface IOptionExerciseModel
{
/// <summary>
/// Model the option exercise
/// </summary>
/// <param name="option">Option we're trading this order</param>
/// <param name="order">Order to update</param>
/// <returns>Order fill information detailing the average price and quantity filled.</returns>
IEnumerable<OrderEvent> OptionExercise(Option option, OptionExerciseOrder order);
}
}
@@ -0,0 +1,48 @@
/*
* 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 Python.Runtime;
using QuantConnect.Python;
using QuantConnect.Securities.Option;
using System.Collections.Generic;
namespace QuantConnect.Orders.OptionExercise
{
/// <summary>
/// Python wrapper for custom option exercise models
/// </summary>
public class OptionExerciseModelPythonWrapper: BasePythonWrapper<IOptionExerciseModel>, IOptionExerciseModel
{
/// <summary>
/// Creates a new instance
/// </summary>
/// <param name="model">The python model to wrapp</param>
public OptionExerciseModelPythonWrapper(PyObject model)
: base(model)
{
}
/// <summary>
/// Performs option exercise for the option security class.
/// </summary>
/// <param name="option">Option we're trading this order</param>
/// <param name="order">Order to update</param>
public IEnumerable<OrderEvent> OptionExercise(Option option, OptionExerciseOrder order)
{
return InvokeMethodAndEnumerate<OrderEvent>(nameof(OptionExercise), option, order);
}
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
* 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;
using QuantConnect.Securities;
using QuantConnect.Securities.Option;
namespace QuantConnect.Orders
{
/// <summary>
/// Option exercise order type definition
/// </summary>
public class OptionExerciseOrder : Order
{
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
public OptionExerciseOrder()
{
}
/// <summary>
/// New option exercise order constructor. We model option exercising as an underlying asset long/short order with strike equal to limit price.
/// This means that by exercising a call we get into long asset position, by exercising a put we get into short asset position.
/// </summary>
/// <param name="symbol">Option symbol we're seeking to exercise</param>
/// <param name="quantity">Quantity of the option we're seeking to exercise. Must be a positive value.</param>
/// <param name="time">Time the order was placed</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
public OptionExerciseOrder(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null)
: base(symbol, quantity, time, tag, properties)
{
}
/// <summary>
/// Option Exercise Order Type
/// </summary>
public override OrderType Type
{
get { return OrderType.OptionExercise; }
}
/// <summary>
/// Gets the order value in option contracts quoted in options's currency
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected override decimal GetValueImpl(Security security)
{
var option = (Option)security;
return option.GetExerciseQuantity(Quantity) * Price / option.SymbolProperties.ContractMultiplier;
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public override Order Clone()
{
var order = new OptionExerciseOrder();
CopyTo(order);
return order;
}
}
}
+483
View File
@@ -0,0 +1,483 @@
/*
* 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.ComponentModel;
using System.Linq;
using System.Threading;
using Newtonsoft.Json;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Securities.Positions;
namespace QuantConnect.Orders
{
/// <summary>
/// Order struct for placing new trade
/// </summary>
public abstract class Order
{
private volatile int _incrementalId;
private decimal _quantity;
private decimal _price;
private int _id;
/// <summary>
/// Order ID.
/// </summary>
[JsonProperty(PropertyName = "id")]
public int Id
{
get => _id;
internal set
{
_id = value;
if (_id != 0 && GroupOrderManager != null)
{
lock (GroupOrderManager.OrderIds)
{
GroupOrderManager.OrderIds.Add(_id);
}
}
}
}
/// <summary>
/// Order id to process before processing this order.
/// </summary>
[JsonProperty(PropertyName = "contingentId")]
public int ContingentId { get; internal set; }
/// <summary>
/// Brokerage Id for this order for when the brokerage splits orders into multiple pieces
/// </summary>
[JsonProperty(PropertyName = "brokerId")]
public List<string> BrokerId { get; internal set; }
/// <summary>
/// Symbol of the Asset
/// </summary>
[JsonProperty(PropertyName = "symbol")]
public Symbol Symbol { get; internal set; }
/// <summary>
/// Price of the Order.
/// </summary>
[JsonProperty(PropertyName = "price")]
public decimal Price
{
get { return _price; }
internal set { _price = value.Normalize(); }
}
/// <summary>
/// Currency for the order price
/// </summary>
[JsonProperty(PropertyName = "priceCurrency")]
public string PriceCurrency { get; internal set; }
/// <summary>
/// Gets the utc time the order was created.
/// </summary>
[JsonProperty(PropertyName = "time")]
public DateTime Time { get; internal set; }
/// <summary>
/// Gets the utc time this order was created. Alias for <see cref="Time"/>
/// </summary>
[JsonProperty(PropertyName = "createdTime")]
public DateTime CreatedTime => Time;
/// <summary>
/// Gets the utc time the last fill was received, or null if no fills have been received
/// </summary>
[JsonProperty(PropertyName = "lastFillTime", NullValueHandling = NullValueHandling.Ignore)]
public DateTime? LastFillTime { get; internal set; }
/// <summary>
/// Gets the utc time this order was last updated, or null if the order has not been updated.
/// </summary>
[JsonProperty(PropertyName = "lastUpdateTime", NullValueHandling = NullValueHandling.Ignore)]
public DateTime? LastUpdateTime { get; internal set; }
/// <summary>
/// Gets the utc time this order was canceled, or null if the order was not canceled.
/// </summary>
[JsonProperty(PropertyName = "canceledTime", NullValueHandling = NullValueHandling.Ignore)]
public DateTime? CanceledTime { get; internal set; }
/// <summary>
/// Number of shares to execute.
/// </summary>
[JsonProperty(PropertyName = "quantity")]
public virtual decimal Quantity
{
get { return _quantity; }
internal set { _quantity = value.Normalize(); }
}
/// <summary>
/// Order Type
/// </summary>
[JsonProperty(PropertyName = "type")]
public abstract OrderType Type { get; }
/// <summary>
/// Status of the Order
/// </summary>
[JsonProperty(PropertyName = "status")]
public OrderStatus Status { get; set; }
/// <summary>
/// Order Time In Force
/// </summary>
[JsonIgnore]
public TimeInForce TimeInForce => Properties.TimeInForce;
/// <summary>
/// Tag the order with some custom data
/// </summary>
[JsonProperty(PropertyName = "tag" ,DefaultValueHandling = DefaultValueHandling.Ignore)]
public string Tag { get; internal set; }
/// <summary>
/// Additional properties of the order
/// </summary>
[JsonProperty(PropertyName = "properties")]
public IOrderProperties Properties { get; private set; }
/// <summary>
/// The symbol's security type
/// </summary>
[JsonProperty(PropertyName = "securityType")]
public SecurityType SecurityType => Symbol.ID.SecurityType;
/// <summary>
/// Order Direction Property based off Quantity.
/// </summary>
[JsonProperty(PropertyName = "direction")]
public OrderDirection Direction
{
get
{
if (Quantity > 0)
{
return OrderDirection.Buy;
}
if (Quantity < 0)
{
return OrderDirection.Sell;
}
return OrderDirection.Hold;
}
}
/// <summary>
/// Get the absolute quantity for this order
/// </summary>
[JsonIgnore]
public decimal AbsoluteQuantity => Math.Abs(Quantity);
/// <summary>
/// Deprecated
/// </summary>
[JsonProperty(PropertyName = "value"), Obsolete("Please use Order.GetValue(security) or security.Holdings.HoldingsValue")]
public decimal Value => Quantity * Price;
/// <summary>
/// Gets the price data at the time the order was submitted
/// </summary>
[JsonProperty(PropertyName = "orderSubmissionData")]
public OrderSubmissionData OrderSubmissionData { get; internal set; }
/// <summary>
/// Returns true if the order is a marketable order.
/// </summary>
[JsonProperty(PropertyName = "isMarketable")]
public bool IsMarketable
{
get
{
if (Type == OrderType.Limit)
{
// check if marketable limit order using bid/ask prices
var limitOrder = (LimitOrder)this;
return OrderSubmissionData != null &&
(Direction == OrderDirection.Buy && limitOrder.LimitPrice >= OrderSubmissionData.AskPrice ||
Direction == OrderDirection.Sell && limitOrder.LimitPrice <= OrderSubmissionData.BidPrice);
}
return Type == OrderType.Market || Type == OrderType.ComboMarket;
}
}
/// <summary>
/// Manager for the orders in the group if this is a combo order
/// </summary>
[JsonProperty(PropertyName = "groupOrderManager", DefaultValueHandling = DefaultValueHandling.Ignore)]
public GroupOrderManager GroupOrderManager { get; set; }
/// <summary>
/// The adjustment mode used on the order fill price
/// </summary>
[JsonProperty(PropertyName = "priceAdjustmentMode")]
public DataNormalizationMode PriceAdjustmentMode { get; set; }
/// <summary>
/// Added a default constructor for JSON Deserialization:
/// </summary>
protected Order()
{
Time = new DateTime();
PriceCurrency = string.Empty;
Symbol = Symbol.Empty;
Status = OrderStatus.None;
Tag = string.Empty;
BrokerId = new List<string>();
Properties = new OrderProperties();
GroupOrderManager = null;
}
/// <summary>
/// New order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="groupOrderManager">Manager for the orders in the group if this is a combo order</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
protected Order(Symbol symbol, decimal quantity, DateTime time, GroupOrderManager groupOrderManager, string tag = "",
IOrderProperties properties = null)
{
Time = time;
PriceCurrency = string.Empty;
Quantity = quantity;
Symbol = symbol;
Status = OrderStatus.None;
Tag = tag;
BrokerId = new List<string>();
Properties = properties ?? new OrderProperties();
GroupOrderManager = groupOrderManager;
}
/// <summary>
/// New order constructor
/// </summary>
/// <param name="symbol">Symbol asset we're seeking to trade</param>
/// <param name="quantity">Quantity of the asset we're seeking to trade</param>
/// <param name="time">Time the order was placed</param>
/// <param name="tag">User defined data tag for this order</param>
/// <param name="properties">The order properties for this order</param>
protected Order(Symbol symbol, decimal quantity, DateTime time, string tag = "", IOrderProperties properties = null)
: this(symbol, quantity, time, null, tag, properties)
{
}
/// <summary>
/// Creates an enumerable containing each position resulting from executing this order.
/// </summary>
/// <remarks>
/// This is provided in anticipation of a new combo order type that will need to override this method,
/// returning a position for each 'leg' of the order.
/// </remarks>
/// <returns>An enumerable of positions matching the results of executing this order</returns>
public virtual IEnumerable<IPosition> CreatePositions(SecurityManager securities)
{
var security = securities[Symbol];
yield return new Position(security, Quantity);
}
/// <summary>
/// Gets the value of this order at the given market price in units of the account currency
/// NOTE: Some order types derive value from other parameters, such as limit prices
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
/// <returns>The value of this order given the current market price</returns>
/// <remarks>TODO: we should remove this. Only used in tests</remarks>
public decimal GetValue(Security security)
{
var value = GetValueImpl(security);
return value*security.QuoteCurrency.ConversionRate*security.SymbolProperties.ContractMultiplier;
}
/// <summary>
/// Gets the order value in units of the security's quote currency for a single unit.
/// A single unit here is a single share of stock, or a single barrel of oil, or the
/// cost of a single share in an option contract.
/// </summary>
/// <param name="security">The security matching this order's symbol</param>
protected abstract decimal GetValueImpl(Security security);
/// <summary>
/// Gets the default tag for this order
/// </summary>
/// <returns>The default tag</returns>
public virtual string GetDefaultTag()
{
return string.Empty;
}
/// <summary>
/// Gets a new unique incremental id for this order
/// </summary>
/// <returns>Returns a new id for this order</returns>
internal int GetNewId()
{
return Interlocked.Increment(ref _incrementalId);
}
/// <summary>
/// Modifies the state of this order to match the update request
/// </summary>
/// <param name="request">The request to update this order object</param>
public virtual void ApplyUpdateOrderRequest(UpdateOrderRequest request)
{
if (request.OrderId != Id)
{
throw new ArgumentException("Attempted to apply updates to the incorrect order!");
}
if (request.Quantity.HasValue)
{
Quantity = request.Quantity.Value;
}
if (request.Tag != null)
{
Tag = request.Tag;
}
}
/// <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.Order.ToString(this);
}
/// <summary>
/// Creates a deep-copy clone of this order
/// </summary>
/// <returns>A copy of this order</returns>
public abstract Order Clone();
/// <summary>
/// Copies base Order properties to the specified order
/// </summary>
/// <param name="order">The target of the copy</param>
protected void CopyTo(Order order)
{
order.Id = Id;
// The group order manager has to be set before the quantity,
// since combo orders might need it to calculate the quantity in the Quantity setter.
order.GroupOrderManager = GroupOrderManager;
order.Time = Time;
order.LastFillTime = LastFillTime;
order.LastUpdateTime = LastUpdateTime;
order.CanceledTime = CanceledTime;
order.BrokerId = BrokerId.ToList();
order.ContingentId = ContingentId;
order.Price = Price;
order.PriceCurrency = PriceCurrency;
order.Quantity = Quantity;
order.Status = Status;
order.Symbol = Symbol;
order.Tag = Tag;
order.Properties = Properties.Clone();
order.OrderSubmissionData = OrderSubmissionData?.Clone();
order.PriceAdjustmentMode = PriceAdjustmentMode;
}
/// <summary>
/// Creates an <see cref="Order"/> to match the specified <paramref name="request"/>
/// </summary>
/// <param name="request">The <see cref="SubmitOrderRequest"/> to create an order for</param>
/// <returns>The <see cref="Order"/> that matches the request</returns>
public static Order CreateOrder(SubmitOrderRequest request)
{
return CreateOrder(request.OrderId, request.OrderType, request.Symbol, request.Quantity, request.Time,
request.Tag, request.OrderProperties, request.LimitPrice, request.StopPrice, request.TriggerPrice, request.TrailingAmount,
request.TrailingAsPercentage, request.GroupOrderManager);
}
private static Order CreateOrder(int orderId, OrderType type, Symbol symbol, decimal quantity, DateTime time,
string tag, IOrderProperties properties, decimal limitPrice, decimal stopPrice, decimal triggerPrice, decimal trailingAmount,
bool trailingAsPercentage, GroupOrderManager groupOrderManager)
{
Order order;
switch (type)
{
case OrderType.Market:
order = new MarketOrder(symbol, quantity, time, tag, properties);
break;
case OrderType.Limit:
order = new LimitOrder(symbol, quantity, limitPrice, time, tag, properties);
break;
case OrderType.StopMarket:
order = new StopMarketOrder(symbol, quantity, stopPrice, time, tag, properties);
break;
case OrderType.StopLimit:
order = new StopLimitOrder(symbol, quantity, stopPrice, limitPrice, time, tag, properties);
break;
case OrderType.TrailingStop:
order = new TrailingStopOrder(symbol, quantity, stopPrice, trailingAmount, trailingAsPercentage, time, tag, properties);
break;
case OrderType.LimitIfTouched:
order = new LimitIfTouchedOrder(symbol, quantity, triggerPrice, limitPrice, time, tag, properties);
break;
case OrderType.MarketOnOpen:
order = new MarketOnOpenOrder(symbol, quantity, time, tag, properties);
break;
case OrderType.MarketOnClose:
order = new MarketOnCloseOrder(symbol, quantity, time, tag, properties);
break;
case OrderType.OptionExercise:
order = new OptionExerciseOrder(symbol, quantity, time, tag, properties);
break;
case OrderType.ComboLimit:
order = new ComboLimitOrder(symbol, quantity, limitPrice, time, groupOrderManager, tag, properties);
break;
case OrderType.ComboLegLimit:
order = new ComboLegLimitOrder(symbol, quantity, limitPrice, time, groupOrderManager, tag, properties);
break;
case OrderType.ComboMarket:
order = new ComboMarketOrder(symbol, quantity, time, groupOrderManager, tag, properties);
break;
default:
throw new ArgumentOutOfRangeException();
}
order.Status = OrderStatus.New;
order.Id = orderId;
return order;
}
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
* 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.ComponentModel;
namespace QuantConnect.Orders
{
/// <summary>
/// Specifies the possible error states during presubmission checks
/// </summary>
public enum OrderError
{
/// <summary>
/// Order has already been filled and cannot be modified (-8)
/// </summary>
[Description("Order has already been filled and cannot be modified")]
CanNotUpdateFilledOrder = -8,
/// <summary>
/// General error in order (-7)
/// </summary>
[Description("General error in order")]
GeneralError = -7,
/// <summary>
/// Order timestamp error. Order appears to be executing in the future (-6)
/// </summary>
[Description("Order timestamp error. Order appears to be executing in the future")]
TimestampError = -6,
/// <summary>
/// Exceeded maximum allowed orders for one analysis period (-5)
/// </summary>
[Description("Exceeded maximum allowed orders for one analysis period")]
MaxOrdersExceeded = -5,
/// <summary>
/// Insufficient capital to execute order (-4)
/// </summary>
[Description("Insufficient capital to execute order")]
InsufficientCapital = -4,
/// <summary>
/// Attempting market order outside of market hours (-3)
/// </summary>
[Description("Attempting market order outside of market hours")]
MarketClosed = -3,
/// <summary>
/// There is no data yet for this security - please wait for data (market order price not available yet) (-2)
/// </summary>
[Description("There is no data yet for this security - please wait for data (market order price not available yet)")]
NoData = -2,
/// <summary>
/// Order quantity must not be zero (-1)
/// </summary>
[Description("Order quantity must not be zero")]
ZeroQuantity = -1,
/// <summary>
/// The order is OK (0)
/// </summary>
[Description("The order is OK")]
None = 0
}
}
+371
View File
@@ -0,0 +1,371 @@
/*
* 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.ComponentModel;
using Newtonsoft.Json;
using ProtoBuf;
using QuantConnect.Orders.Fees;
using QuantConnect.Orders.Serialization;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Order Event - Messaging class signifying a change in an order state and record the change in the user's algorithm portfolio
/// </summary>
[ProtoContract(SkipConstructor = true)]
public class OrderEvent
{
private decimal _fillPrice;
private decimal _fillQuantity;
private decimal _quantity;
private decimal? _limitPrice;
private decimal? _triggerPrice;
private decimal? _stopPrice;
private decimal? _trailingAmount;
private bool? _trailingAsPercentage;
/// <summary>
/// Id of the order this event comes from.
/// </summary>
[ProtoMember(1)]
public int OrderId { get; set; }
/// <summary>
/// The unique order event id for each order
/// </summary>
[ProtoMember(2)]
public int Id { get; set; }
/// <summary>
/// Easy access to the order symbol associated with this event.
/// </summary>
[ProtoMember(3)]
public Symbol Symbol { get; set; }
/// <summary>
/// The date and time of this event (UTC).
/// </summary>
[ProtoMember(4)]
public DateTime UtcTime { get; set; }
/// <summary>
/// Status message of the order.
/// </summary>
[ProtoMember(5)]
public OrderStatus Status { get; set; }
/// <summary>
/// The fee associated with the order
/// </summary>
[ProtoMember(6)]
public OrderFee OrderFee { get; set; }
/// <summary>
/// Fill price information about the order
/// </summary>
[ProtoMember(7)]
public decimal FillPrice
{
get { return _fillPrice; }
set { _fillPrice = value.Normalize(); }
}
/// <summary>
/// Currency for the fill price
/// </summary>
[ProtoMember(8)]
public string FillPriceCurrency { get; set; }
/// <summary>
/// Number of shares of the order that was filled in this event.
/// </summary>
[ProtoMember(9)]
public decimal FillQuantity
{
get { return _fillQuantity; }
set { _fillQuantity = value.Normalize(); }
}
/// <summary>
/// Public Property Absolute Getter of Quantity -Filled
/// </summary>
[JsonIgnore]
public decimal AbsoluteFillQuantity => Math.Abs(FillQuantity);
/// <summary>
/// Order direction.
/// </summary>
[ProtoMember(10)]
public OrderDirection Direction { get; set; }
/// <summary>
/// Any message from the exchange.
/// </summary>
[DefaultValue(""), JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[ProtoMember(11)]
public string Message { get; set; }
/// <summary>
/// True if the order event is an assignment
/// </summary>
[ProtoMember(12)]
public bool IsAssignment { get; set; }
/// <summary>
/// The current stop price
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[ProtoMember(13)]
public decimal? StopPrice
{
get { return _stopPrice; }
set
{
if (value.HasValue)
{
_stopPrice = value.Value.Normalize();
}
}
}
/// <summary>
/// The current trigger price
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[ProtoMember(14)]
public decimal? TriggerPrice
{
get { return _triggerPrice; }
set
{
if (value.HasValue)
{
_triggerPrice = value.Value.Normalize();
}
}
}
/// <summary>
/// The current limit price
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[ProtoMember(15)]
public decimal? LimitPrice
{
get { return _limitPrice; }
set
{
if (value.HasValue)
{
_limitPrice = value.Value.Normalize();
}
}
}
/// <summary>
/// The current order quantity
/// </summary>
[ProtoMember(16)]
public decimal Quantity
{
get { return _quantity; }
set { _quantity = value.Normalize(); }
}
/// <summary>
/// True if the order event's option is In-The-Money (ITM)
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[ProtoMember(17)]
public bool IsInTheMoney { get; set; }
/// <summary>
/// The trailing stop amount
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[ProtoMember(18)]
public decimal? TrailingAmount
{
get { return _trailingAmount; }
set
{
if (value.HasValue)
{
_trailingAmount = value.Value.Normalize();
}
}
}
/// <summary>
/// Whether the <see cref="TrailingAmount"/> is a percentage or an absolute currency value
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
[ProtoMember(19)]
public bool? TrailingAsPercentage
{
get { return _trailingAsPercentage; }
set
{
if (value.HasValue)
{
_trailingAsPercentage = value.Value;
}
}
}
/// <summary>
/// The order ticket associated to the order
/// </summary>
[JsonIgnore]
public OrderTicket Ticket { get; set; }
/// <summary>
/// Order Event empty constructor required for json converter
/// </summary>
public OrderEvent()
{
}
/// <summary>
/// Order Event Constructor.
/// </summary>
/// <param name="orderId">Id of the parent order</param>
/// <param name="symbol">Asset Symbol</param>
/// <param name="utcTime">Date/time of this event</param>
/// <param name="status">Status of the order</param>
/// <param name="direction">The direction of the order this event belongs to</param>
/// <param name="fillPrice">Fill price information if applicable.</param>
/// <param name="fillQuantity">Fill quantity</param>
/// <param name="orderFee">The order fee</param>
/// <param name="message">Message from the exchange</param>
public OrderEvent(int orderId,
Symbol symbol,
DateTime utcTime,
OrderStatus status,
OrderDirection direction,
decimal fillPrice,
decimal fillQuantity,
OrderFee orderFee,
string message = ""
)
{
OrderId = orderId;
Symbol = symbol;
UtcTime = utcTime;
Status = status;
Direction = direction;
FillPrice = fillPrice;
FillPriceCurrency = string.Empty;
FillQuantity = fillQuantity;
OrderFee = orderFee;
Message = message;
IsAssignment = false;
}
/// <summary>
/// Helper Constructor using Order to Initialize.
/// </summary>
/// <param name="order">Order for this order status</param>
/// <param name="utcTime">Date/time of this event</param>
/// <param name="orderFee">The order fee</param>
/// <param name="message">Message from exchange or QC.</param>
public OrderEvent(Order order, DateTime utcTime, OrderFee orderFee, string message = "")
{
OrderId = order.Id;
Symbol = order.Symbol;
Status = order.Status;
Quantity = order.Quantity;
Direction = order.Direction;
//Initialize to zero, manually set fill quantity
FillQuantity = 0;
FillPrice = 0;
FillPriceCurrency = order.PriceCurrency;
UtcTime = utcTime;
OrderFee = orderFee;
Message = message;
IsAssignment = false;
}
/// <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.OrderEvent.ToString(this);
}
/// <summary>
/// Returns a short string that represents the current object.
/// </summary>
public string ShortToString()
{
return Messages.OrderEvent.ShortToString(this);
}
/// <summary>
/// Returns a clone of the current object.
/// </summary>
/// <returns>The new clone object</returns>
public OrderEvent Clone()
{
return (OrderEvent) MemberwiseClone();
}
/// <summary>
/// Creates a new instance based on the provided serialized order event
/// </summary>
public static OrderEvent FromSerialized(SerializedOrderEvent serializedOrderEvent)
{
var sid = SecurityIdentifier.Parse(serializedOrderEvent.Symbol);
var symbol = new Symbol(sid, sid.Symbol);
var orderFee = OrderFee.Zero;
if (serializedOrderEvent.OrderFeeAmount.HasValue)
{
orderFee = new OrderFee(new CashAmount(serializedOrderEvent.OrderFeeAmount.Value,
serializedOrderEvent.OrderFeeCurrency));
}
var orderEvent = new OrderEvent(serializedOrderEvent.OrderId,
symbol,
DateTime.SpecifyKind(Time.UnixTimeStampToDateTime(serializedOrderEvent.Time), DateTimeKind.Utc),
serializedOrderEvent.Status,
serializedOrderEvent.Direction,
serializedOrderEvent.FillPrice,
serializedOrderEvent.FillQuantity,
orderFee,
serializedOrderEvent.Message)
{
IsAssignment = serializedOrderEvent.IsAssignment,
IsInTheMoney = serializedOrderEvent.IsInTheMoney,
LimitPrice = serializedOrderEvent.LimitPrice,
StopPrice = serializedOrderEvent.StopPrice,
FillPriceCurrency = serializedOrderEvent.FillPriceCurrency,
Id = serializedOrderEvent.OrderEventId,
Quantity = serializedOrderEvent.Quantity
};
return orderEvent;
}
}
}
+80
View File
@@ -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.Collections.Generic;
namespace QuantConnect.Orders
{
/// <summary>
/// Provides extension methods for the <see cref="Order"/> class and for the <see cref="OrderStatus"/> enumeration
/// </summary>
public static class OrderExtensions
{
/// <summary>
/// Determines if the specified status is in a closed state.
/// </summary>
/// <param name="status">The status to check</param>
/// <returns>True if the status is <see cref="OrderStatus.Filled"/>, <see cref="OrderStatus.Canceled"/>, or <see cref="OrderStatus.Invalid"/></returns>
public static bool IsClosed(this OrderStatus status)
{
return status == OrderStatus.Filled
|| status == OrderStatus.Canceled
|| status == OrderStatus.Invalid;
}
/// <summary>
/// Determines if the specified status is in an open state.
/// </summary>
/// <param name="status">The status to check</param>
/// <returns>True if the status is not <see cref="OrderStatus.Filled"/>, <see cref="OrderStatus.Canceled"/>, or <see cref="OrderStatus.Invalid"/></returns>
public static bool IsOpen(this OrderStatus status)
{
return !status.IsClosed();
}
/// <summary>
/// Determines if the specified status is a fill, that is, <see cref="OrderStatus.Filled"/>
/// order <see cref="OrderStatus.PartiallyFilled"/>
/// </summary>
/// <param name="status">The status to check</param>
/// <returns>True if the status is <see cref="OrderStatus.Filled"/> or <see cref="OrderStatus.PartiallyFilled"/>, false otherwise</returns>
public static bool IsFill(this OrderStatus status)
{
return status == OrderStatus.Filled || status == OrderStatus.PartiallyFilled;
}
/// <summary>
/// Determines whether or not the specified order is a limit order
/// </summary>
/// <param name="orderType">The order to check</param>
/// <returns>True if the order is a limit order, false otherwise</returns>
public static bool IsLimitOrder(this OrderType orderType)
{
return orderType == OrderType.Limit
|| orderType == OrderType.StopLimit
|| orderType == OrderType.LimitIfTouched;
}
/// <summary>
/// Determines whether or not the specified order is a stop order
/// </summary>
/// <param name="orderType">The order to check</param>
/// <returns>True if the order is a stop order, false otherwise</returns>
public static bool IsStopOrder(this OrderType orderType)
{
return orderType == OrderType.StopMarket || orderType == OrderType.StopLimit || orderType == OrderType.TrailingStop;
}
}
}
+48
View File
@@ -0,0 +1,48 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Specifies an order field that does not apply to all order types
/// </summary>
public enum OrderField
{
/// <summary>
/// The limit price for a <see cref="LimitOrder"/>, <see cref="StopLimitOrder"/> or <see cref="LimitIfTouchedOrder"/> (0)
/// </summary>
LimitPrice,
/// <summary>
/// The stop price for stop orders (<see cref="StopMarketOrder"/>, <see cref="StopLimitOrder"/>) (1)
/// </summary>
StopPrice,
/// <summary>
/// The trigger price for a <see cref="LimitIfTouchedOrder"/> (2)
/// </summary>
TriggerPrice,
/// <summary>
/// The trailing amount for a <see cref="TrailingStopOrder"/> (3)
/// </summary>
TrailingAmount,
/// <summary>
/// Whether the trailing amount for a <see cref="TrailingStopOrder"/> is a percentage or an absolute currency value (4)
/// </summary>
TrailingAsPercentage
}
}
+387
View File
@@ -0,0 +1,387 @@
/*
* 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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using QuantConnect.Brokerages;
using QuantConnect.Securities;
namespace QuantConnect.Orders
{
/// <summary>
/// Provides an implementation of <see cref="JsonConverter"/> that can deserialize Orders
/// </summary>
public class OrderJsonConverter : JsonConverter
{
/// <summary>
/// Gets a value indicating whether this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON.
/// </summary>
/// <value>
/// <c>true</c> if this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON; otherwise, <c>false</c>.
/// </value>
public override bool CanWrite
{
get { return false; }
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
/// <param name="objectType">Type of the object.</param>
/// <returns>
/// <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
/// </returns>
public override bool CanConvert(Type objectType)
{
return typeof(Order).IsAssignableFrom(objectType);
}
/// <summary>
/// Writes the JSON representation of the object.
/// </summary>
/// <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param><param name="value">The value.</param><param name="serializer">The calling serializer.</param>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException("The OrderJsonConverter does not implement a WriteJson method;.");
}
/// <summary>
/// Reads the JSON representation of the object.
/// </summary>
/// <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param><param name="objectType">Type of the object.</param><param name="existingValue">The existing value of object being read.</param><param name="serializer">The calling serializer.</param>
/// <returns>
/// The object value.
/// </returns>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
var order = CreateOrderFromJObject(jObject);
return order;
}
/// <summary>
/// Create an order from a simple JObject
/// </summary>
/// <param name="jObject"></param>
/// <returns>Order Object</returns>
public static Order CreateOrderFromJObject(JObject jObject)
{
// create order instance based on order type field
var orderType = (OrderType)(jObject["Type"]?.Value<int>() ?? jObject["type"].Value<int>());
var order = CreateOrder(orderType, jObject);
// populate common order properties
order.Id = jObject["Id"]?.Value<int>() ?? jObject["id"].Value<int>();
var jsonStatus = jObject["Status"] ?? jObject["status"];
var jsonTime = jObject["Time"] ?? jObject["time"];
if (jsonStatus.Type == JTokenType.Integer)
{
order.Status = (OrderStatus)jsonStatus.Value<int>();
}
else if (jsonStatus.Type == JTokenType.Null)
{
order.Status = OrderStatus.Canceled;
}
else
{
// The `Status` tag can sometimes appear as a string of the enum value in the LiveResultPacket.
order.Status = (OrderStatus)Enum.Parse(typeof(OrderStatus), jsonStatus.Value<string>(), true);
}
if (jsonTime != null && jsonTime.Type != JTokenType.Null)
{
order.Time = jsonTime.Value<DateTime>();
}
else
{
// `Time` can potentially be null in some LiveResultPacket instances, but
// `CreatedTime` will always be there if `Time` is absent.
order.Time = (jObject["CreatedTime"]?.Value<DateTime>() ?? jObject["createdTime"].Value<DateTime>());
}
var orderSubmissionData = jObject["OrderSubmissionData"] ?? jObject["orderSubmissionData"];
if (orderSubmissionData != null && orderSubmissionData.Type != JTokenType.Null)
{
var bidPrice = orderSubmissionData["BidPrice"]?.Value<decimal>() ?? orderSubmissionData["bidPrice"].Value<decimal>();
var askPrice = orderSubmissionData["AskPrice"]?.Value<decimal>() ?? orderSubmissionData["askPrice"].Value<decimal>();
var lastPrice = orderSubmissionData["LastPrice"]?.Value<decimal>() ?? orderSubmissionData["lastPrice"].Value<decimal>();
order.OrderSubmissionData = new OrderSubmissionData(bidPrice, askPrice, lastPrice);
}
var priceAdjustmentMode = jObject["PriceAdjustmentMode"] ?? jObject["priceAdjustmentMode"];
if (priceAdjustmentMode != null && priceAdjustmentMode.Type != JTokenType.Null)
{
var value = priceAdjustmentMode.Value<int>();
order.PriceAdjustmentMode = (DataNormalizationMode)value;
}
var lastFillTime = jObject["LastFillTime"] ?? jObject["lastFillTime"];
var lastUpdateTime = jObject["LastUpdateTime"] ?? jObject["lastUpdateTime"];
var canceledTime = jObject["CanceledTime"] ?? jObject["canceledTime"];
if (canceledTime != null && canceledTime.Type != JTokenType.Null)
{
order.CanceledTime = canceledTime.Value<DateTime>();
}
if (lastFillTime != null && lastFillTime.Type != JTokenType.Null)
{
order.LastFillTime = lastFillTime.Value<DateTime>();
}
if (lastUpdateTime != null && lastUpdateTime.Type != JTokenType.Null)
{
order.LastUpdateTime = lastUpdateTime.Value<DateTime>();
}
var tag = jObject["Tag"] ?? jObject["tag"];
if (tag != null && tag.Type != JTokenType.Null)
{
order.Tag = tag.Value<string>();
}
else
{
order.Tag = string.Empty;
}
order.Quantity = jObject["Quantity"]?.Value<decimal>() ?? jObject["quantity"].Value<decimal>();
var orderPrice = jObject["Price"] ?? jObject["price"];
if (orderPrice != null && orderPrice.Type != JTokenType.Null)
{
order.Price = orderPrice.Value<decimal>();
}
else
{
order.Price = default(decimal);
}
var priceCurrency = jObject["PriceCurrency"] ?? jObject["priceCurrency"];
if (priceCurrency != null && priceCurrency.Type != JTokenType.Null)
{
order.PriceCurrency = priceCurrency.Value<string>();
}
order.BrokerId = jObject["BrokerId"]?.Select(x => x.Value<string>()).ToList() ?? jObject["brokerId"].Select(x => x.Value<string>()).ToList();
var jsonContingentId = jObject["ContingentId"] ?? jObject["contingentId"];
if (jsonContingentId != null && jsonContingentId.Type != JTokenType.Null)
{
order.ContingentId = jsonContingentId.Value<int>();
}
var timeInForce = jObject["Properties"]?["TimeInForce"] ?? jObject["TimeInForce"] ?? jObject["Duration"];
if (timeInForce == null)
{
timeInForce = jObject["properties"]?["timeInForce"] ?? jObject["timeInForce"] ?? jObject["duration"];
}
order.Properties.TimeInForce = (timeInForce != null)
? CreateTimeInForce(timeInForce, jObject)
: TimeInForce.GoodTilCanceled;
if (jObject.SelectTokens("Symbol.ID").Any())
{
var sid = SecurityIdentifier.Parse(jObject.SelectTokens("Symbol.ID").Single().Value<string>());
var ticker = jObject.SelectTokens("Symbol.Value").Single().Value<string>();
order.Symbol = new Symbol(sid, ticker);
}
else if (jObject.SelectTokens("symbol.id").Any())
{
var sid = SecurityIdentifier.Parse(jObject.SelectTokens("symbol.id").Single().Value<string>());
var ticker = jObject.SelectTokens("symbol.value").Single().Value<string>();
order.Symbol = new Symbol(sid, ticker);
}
else
{
string market = null;
//does data have market?
var suppliedMarket = jObject.SelectTokens("Symbol.ID.Market") ?? jObject.SelectTokens("symbol.ID.Market");
if (suppliedMarket.Any())
{
market = suppliedMarket.Single().Value<string>();
}
// we only get the security type if we need it, because it might not be there in other cases
var securityType = (SecurityType)(jObject["SecurityType"]?.Value<int>() ?? jObject["securityType"].Value<int>());
var symbolValueUpperCase = jObject.SelectTokens("Symbol.Value");
var symbolValueCamelCase = jObject.SelectTokens("symbol.value");
string ticker = default;
if (symbolValueUpperCase.Any())
{
// provide for backwards compatibility
ticker = symbolValueUpperCase.Single().Value<string>();
}
else if (symbolValueCamelCase.Any())
{
// provide compatibility for orders in camel case
ticker = symbolValueCamelCase.Single().Value<string>();
}
else
{
ticker = jObject["Symbol"]?.Value<string>() ?? jObject["symbol"]?.Value<string>();
}
if (market == null && !SymbolPropertiesDatabase.FromDataFolder().TryGetMarket(ticker, securityType, out market))
{
market = DefaultBrokerageModel.DefaultMarketMap[securityType];
}
order.Symbol = Symbol.Create(ticker, securityType, market);
}
return order;
}
/// <summary>
/// Creates an order of the correct type
/// </summary>
private static Order CreateOrder(OrderType orderType, JObject jObject)
{
Order order;
switch (orderType)
{
case OrderType.Market:
order = new MarketOrder();
break;
case OrderType.Limit:
order = new LimitOrder { LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal) };
break;
case OrderType.StopMarket:
order = new StopMarketOrder
{
StopPrice = jObject["stopPrice"]?.Value<decimal>() ?? jObject["StopPrice"]?.Value<decimal>() ?? default(decimal)
};
break;
case OrderType.StopLimit:
order = new StopLimitOrder
{
LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal),
StopPrice = jObject["stopPrice"]?.Value<decimal>() ?? jObject["StopPrice"]?.Value<decimal>() ?? default(decimal)
};
break;
case OrderType.TrailingStop:
order = new TrailingStopOrder
{
StopPrice = jObject["StopPrice"]?.Value<decimal>() ?? jObject["stopPrice"]?.Value<decimal>() ?? default(decimal),
TrailingAmount = jObject["TrailingAmount"]?.Value<decimal>() ?? jObject["trailingAmount"]?.Value<decimal>() ?? default(decimal),
TrailingAsPercentage = jObject["TrailingAsPercentage"]?.Value<bool>() ?? jObject["trailingAsPercentage"]?.Value<bool>() ?? default(bool)
};
break;
case OrderType.LimitIfTouched:
order = new LimitIfTouchedOrder
{
LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal),
TriggerPrice = jObject["TriggerPrice"]?.Value<decimal>() ?? jObject["triggerPrice"]?.Value<decimal>() ?? default(decimal)
};
break;
case OrderType.MarketOnOpen:
order = new MarketOnOpenOrder();
break;
case OrderType.MarketOnClose:
order = new MarketOnCloseOrder();
break;
case OrderType.OptionExercise:
order = new OptionExerciseOrder();
break;
case OrderType.ComboMarket:
order = new ComboMarketOrder() { GroupOrderManager = DeserializeGroupOrderManager(jObject) };
break;
case OrderType.ComboLimit:
order = new ComboLimitOrder() { GroupOrderManager = DeserializeGroupOrderManager(jObject) };
break;
case OrderType.ComboLegLimit:
order = new ComboLegLimitOrder
{
GroupOrderManager = DeserializeGroupOrderManager(jObject),
LimitPrice = jObject["LimitPrice"]?.Value<decimal>() ?? jObject["limitPrice"]?.Value<decimal>() ?? default(decimal)
};
break;
default:
throw new ArgumentOutOfRangeException();
}
return order;
}
/// <summary>
/// Creates a Time In Force of the correct type
/// </summary>
private static TimeInForce CreateTimeInForce(JToken timeInForce, JObject jObject)
{
// for backward-compatibility support deserialization of old JSON format
if (timeInForce is JValue)
{
var value = timeInForce.Value<int>();
switch (value)
{
case 0:
return TimeInForce.GoodTilCanceled;
case 1:
return TimeInForce.Day;
case 2:
var expiry = jObject["DurationValue"].Value<DateTime>();
return TimeInForce.GoodTilDate(expiry);
default:
throw new Exception($"Unknown time in force value: {value}");
}
}
// convert with TimeInForceJsonConverter
return timeInForce.ToObject<TimeInForce>();
}
/// <summary>
/// Deserializes the GroupOrderManager from the JSON object
/// </summary>
private static GroupOrderManager DeserializeGroupOrderManager(JObject jObject)
{
var groupOrderManagerJObject = jObject["GroupOrderManager"] ?? jObject["groupOrderManager"];
// this should never happen
if (groupOrderManagerJObject == null)
{
throw new ArgumentException("OrderJsonConverter.DeserializeGroupOrderManager(): JObject does not have a GroupOrderManager");
}
var result = new GroupOrderManager(
groupOrderManagerJObject["Id"]?.Value<int>() ?? groupOrderManagerJObject["id"].Value<int>(),
groupOrderManagerJObject["Count"]?.Value<int>() ?? groupOrderManagerJObject["count"].Value<int>(),
groupOrderManagerJObject["Quantity"]?.Value<decimal>() ?? groupOrderManagerJObject["quantity"].Value<decimal>(),
groupOrderManagerJObject["LimitPrice"]?.Value<decimal>() ?? groupOrderManagerJObject["limitPrice"].Value<decimal>()
);
foreach (var orderId in (groupOrderManagerJObject["OrderIds"]?.Values<int>() ?? groupOrderManagerJObject["orderIds"].Values<int>()))
{
result.OrderIds.Add(orderId);
}
return result;
}
}
}
+62
View File
@@ -0,0 +1,62 @@
/*
* 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 Newtonsoft.Json;
using QuantConnect.Interfaces;
namespace QuantConnect.Orders
{
/// <summary>
/// Contains additional properties and settings for an order
/// </summary>
public class OrderProperties : IOrderProperties
{
/// <summary>
/// Defines the length of time over which an order will continue working before it is cancelled
/// </summary>
public TimeInForce TimeInForce { get; set; }
/// <summary>
/// Defines the exchange name for a particular market
/// </summary>
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public Exchange Exchange { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="OrderProperties"/> class
/// </summary>
public OrderProperties()
{
TimeInForce = TimeInForce.GoodTilCanceled;
}
/// <summary>
/// Initializes a new instance of the <see cref="OrderProperties"/> class, with exchange param
///<param name="exchange">Exchange name for market</param>
/// </summary>
public OrderProperties(Exchange exchange) : this()
{
Exchange = exchange;
}
/// <summary>
/// Returns a new instance clone of this object
/// </summary>
public virtual IOrderProperties Clone()
{
return (OrderProperties)MemberwiseClone();
}
}
}
+118
View File
@@ -0,0 +1,118 @@
/*
* 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.Orders
{
/// <summary>
/// Represents a request to submit, update, or cancel an order
/// </summary>
public abstract class OrderRequest
{
/// <summary>
/// Gets the type of this order request
/// </summary>
public abstract OrderRequestType OrderRequestType
{
get;
}
/// <summary>
/// Gets the status of this request
/// </summary>
public OrderRequestStatus Status
{
get; private set;
}
/// <summary>
/// Gets the UTC time the request was created
/// </summary>
public DateTime Time
{
get; private set;
}
/// <summary>
/// Gets the order id the request acts on
/// </summary>
public int OrderId
{
get; protected set;
}
/// <summary>
/// Gets a tag for this request
/// </summary>
public string Tag
{
get; private set;
}
/// <summary>
/// Gets the response for this request. If this request was never processed then this
/// will equal <see cref="OrderResponse.Unprocessed"/>. This value is never equal to null.
/// </summary>
public OrderResponse Response
{
get; private set;
}
/// <summary>
/// Initializes a new instance of the <see cref="OrderRequest"/> class
/// </summary>
/// <param name="time">The time this request was created</param>
/// <param name="orderId">The order id this request acts on, specify zero for <see cref="SubmitOrderRequest"/></param>
/// <param name="tag">A custom tag for the request</param>
protected OrderRequest(DateTime time, int orderId, string tag)
{
Time = time;
OrderId = orderId;
Tag = tag;
Response = OrderResponse.Unprocessed;
Status = OrderRequestStatus.Unprocessed;
}
/// <summary>
/// Sets the <see cref="Response"/> for this request
/// </summary>
/// <param name="response">The response to this request</param>
/// <param name="status">The current status of this request</param>
public void SetResponse(OrderResponse response, OrderRequestStatus status = OrderRequestStatus.Error)
{
if (response == null)
{
throw new ArgumentNullException(nameof(response), "Response can not be null");
}
// if the response is an error, ignore the input status
Status = response.IsError ? OrderRequestStatus.Error : status;
Response = response;
}
/// <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.OrderRequest.ToString(this);
}
}
}
+43
View File
@@ -0,0 +1,43 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Specifies the status of a request
/// </summary>
public enum OrderRequestStatus
{
/// <summary>
/// This is an unprocessed request (0)
/// </summary>
Unprocessed,
/// <summary>
/// This request is partially processed (1)
/// </summary>
Processing,
/// <summary>
/// This request has been completely processed (2)
/// </summary>
Processed,
/// <summary>
/// This request encountered an error (3)
/// </summary>
Error
}
}
+38
View File
@@ -0,0 +1,38 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Specifies the type of <see cref="OrderRequest"/>
/// </summary>
public enum OrderRequestType
{
/// <summary>
/// The request is a <see cref="SubmitOrderRequest"/> (0)
/// </summary>
Submit,
/// <summary>
/// The request is a <see cref="UpdateOrderRequest"/> (1)
/// </summary>
Update,
/// <summary>
/// The request is a <see cref="CancelOrderRequest"/> (2)
/// </summary>
Cancel
}
}
+176
View File
@@ -0,0 +1,176 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Represents a response to an <see cref="OrderRequest"/>. See <see cref="OrderRequest.Response"/> property for
/// a specific request's response value
/// </summary>
public class OrderResponse
{
/// <summary>
/// Gets the order id
/// </summary>
public int OrderId
{
get; private set;
}
/// <summary>
/// Gets the error message if the <see cref="ErrorCode"/> does not equal <see cref="OrderResponseErrorCode.None"/>, otherwise
/// gets <see cref="string.Empty"/>
/// </summary>
public string ErrorMessage
{
get; private set;
}
/// <summary>
/// Gets the error code for this response.
/// </summary>
public OrderResponseErrorCode ErrorCode
{
get; private set;
}
/// <summary>
/// Gets true if this response represents a successful request, false otherwise
/// If this is an unprocessed response, IsSuccess will return false.
/// </summary>
public bool IsSuccess
{
get { return IsProcessed && !IsError; }
}
/// <summary>
/// Gets true if this response represents an error, false otherwise
/// </summary>
public bool IsError
{
get { return IsProcessed && ErrorCode != OrderResponseErrorCode.None; }
}
/// <summary>
/// Gets true if this response has been processed, false otherwise
/// </summary>
public bool IsProcessed
{
get { return this != Unprocessed; }
}
/// <summary>
/// Initializes a new instance of the <see cref="OrderResponse"/> class
/// </summary>
/// <param name="orderId">The order id</param>
/// <param name="errorCode">The error code of the response, specify <see cref="OrderResponseErrorCode.None"/> for no error</param>
/// <param name="errorMessage">The error message, applies only if the <paramref name="errorCode"/> does not equal <see cref="OrderResponseErrorCode.None"/></param>
private OrderResponse(int orderId, OrderResponseErrorCode errorCode, string errorMessage)
{
OrderId = orderId;
ErrorCode = errorCode;
if (errorCode != OrderResponseErrorCode.None)
{
ErrorMessage = errorMessage ?? Messages.OrderResponse.DefaultErrorMessage;
}
}
/// <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.OrderResponse.ToString(this);
}
#region Statics - implicit(int), Unprocessed constant, response factory methods
/// <summary>
/// Gets an <see cref="OrderResponse"/> for a request that has not yet been processed
/// </summary>
public static readonly OrderResponse Unprocessed = new OrderResponse(int.MinValue, OrderResponseErrorCode.None,
Messages.OrderResponse.UnprocessedOrderResponseErrorMessage);
/// <summary>
/// Helper method to create a successful response from a request
/// </summary>
public static OrderResponse Success(OrderRequest request)
{
return new OrderResponse(request.OrderId, OrderResponseErrorCode.None, null);
}
/// <summary>
/// Helper method to create an error response from a request
/// </summary>
public static OrderResponse Error(OrderRequest request, OrderResponseErrorCode errorCode, string errorMessage)
{
return new OrderResponse(request.OrderId, errorCode, errorMessage);
}
/// <summary>
/// Helper method to create an error response due to an invalid order status
/// </summary>
public static OrderResponse InvalidStatus(OrderRequest request, Order order)
{
return Error(request, OrderResponseErrorCode.InvalidOrderStatus, Messages.OrderResponse.InvalidStatus(request, order));
}
/// <summary>
/// Helper method to create an error response due to the "New" order status
/// </summary>
public static OrderResponse InvalidNewStatus(OrderRequest request, Order order)
{
return Error(request, OrderResponseErrorCode.InvalidNewOrderStatus, Messages.OrderResponse.InvalidNewStatus(request, order));
}
/// <summary>
/// Helper method to create an error response due to a bad order id
/// </summary>
public static OrderResponse UnableToFindOrder(OrderRequest request)
{
return Error(request, OrderResponseErrorCode.UnableToFindOrder, Messages.OrderResponse.UnableToFindOrder(request));
}
/// <summary>
/// Helper method to create an error response due to a zero order quantity
/// </summary>
public static OrderResponse ZeroQuantity(OrderRequest request)
{
return Error(request, OrderResponseErrorCode.OrderQuantityZero, Messages.OrderResponse.ZeroQuantity(request));
}
/// <summary>
/// Helper method to create an error response due to a missing security
/// </summary>
public static OrderResponse MissingSecurity(SubmitOrderRequest request)
{
return Error(request, OrderResponseErrorCode.MissingSecurity, Messages.OrderResponse.MissingSecurity(request));
}
/// <summary>
/// Helper method to create an error response due to algorithm still in warmup mode
/// </summary>
public static OrderResponse WarmingUp(OrderRequest request)
{
return Error(request, OrderResponseErrorCode.AlgorithmWarmingUp, Messages.OrderResponse.WarmingUp(request));
}
#endregion
}
}
+204
View File
@@ -0,0 +1,204 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Error detail code
/// </summary>
public enum OrderResponseErrorCode
{
/// <summary>
/// No error (0)
/// </summary>
None = 0,
/// <summary>
/// Unknown error (-1)
/// </summary>
ProcessingError = -1,
/// <summary>
/// Cannot submit because order already exists (-2)
/// </summary>
OrderAlreadyExists = -2,
/// <summary>
/// Not enough money to to submit order (-3)
/// </summary>
InsufficientBuyingPower = -3,
/// <summary>
/// Internal logic invalidated submit order (-4)
/// </summary>
BrokerageModelRefusedToSubmitOrder = -4,
/// <summary>
/// Brokerage submit error (-5)
/// </summary>
BrokerageFailedToSubmitOrder = -5,
/// <summary>
/// Brokerage update error (-6)
/// </summary>
BrokerageFailedToUpdateOrder = -6,
/// <summary>
/// Internal logic invalidated update order (-7)
/// </summary>
BrokerageHandlerRefusedToUpdateOrder = -7,
/// <summary>
/// Brokerage cancel error (-8)
/// </summary>
BrokerageFailedToCancelOrder = -8,
/// <summary>
/// Only pending orders can be canceled (-9)
/// </summary>
InvalidOrderStatus = -9,
/// <summary>
/// Missing order (-10)
/// </summary>
UnableToFindOrder = -10,
/// <summary>
/// Cannot submit or update orders with zero quantity (-11)
/// </summary>
OrderQuantityZero = -11,
/// <summary>
/// This type of request is unsupported (-12)
/// </summary>
UnsupportedRequestType = -12,
/// <summary>
/// Unknown error during pre order request validation (-13)
/// </summary>
PreOrderChecksError = -13,
/// <summary>
/// Security is missing. Probably did not subscribe (-14)
/// </summary>
MissingSecurity = -14,
/// <summary>
/// Some order types require open exchange (-15)
/// </summary>
ExchangeNotOpen = -15,
/// <summary>
/// Zero security price is probably due to bad data (-16)
/// </summary>
SecurityPriceZero = -16,
/// <summary>
/// Need both currencies in cashbook to trade a pair (-17)
/// </summary>
ForexBaseAndQuoteCurrenciesRequired = -17,
/// <summary>
/// Need conversion rate to account currency (-18)
/// </summary>
ForexConversionRateZero = -18,
/// <summary>
/// Should not attempt trading without at least one data point (-19)
/// </summary>
SecurityHasNoData = -19,
/// <summary>
/// Transaction manager's cache is full (-20)
/// </summary>
ExceededMaximumOrders = -20,
/// <summary>
/// Below buffer time for MOC order to be placed before exchange closes. 15.5 minutes by default (-21)
/// </summary>
MarketOnCloseOrderTooLate = -21,
/// <summary>
/// Request is invalid or null (-22)
/// </summary>
InvalidRequest = -22,
/// <summary>
/// Request was canceled by user (-23)
/// </summary>
RequestCanceled = -23,
/// <summary>
/// All orders are invalidated while algorithm is warming up (-24)
/// </summary>
AlgorithmWarmingUp = -24,
/// <summary>
/// Internal logic invalidated update order (-25)
/// </summary>
BrokerageModelRefusedToUpdateOrder = -25,
/// <summary>
/// Need quote currency in cashbook to trade (-26)
/// </summary>
QuoteCurrencyRequired = -26,
/// <summary>
/// Need conversion rate to account currency (-27)
/// </summary>
ConversionRateZero = -27,
/// <summary>
/// The order's symbol references a non-tradable security (-28)
/// </summary>
NonTradableSecurity = -28,
/// <summary>
/// The order's symbol references a non-exercisable security (-29)
/// </summary>
NonExercisableSecurity = -29,
/// <summary>
/// Cannot submit or update orders with quantity that is less than lot size (-30)
/// </summary>
OrderQuantityLessThanLotSize = -30,
/// <summary>
/// The order's quantity exceeds the max shortable quantity set by the brokerage (-31)
/// </summary>
ExceedsShortableQuantity = -31,
/// <summary>
/// Cannot update/cancel orders with OrderStatus.New (-32)
/// </summary>
InvalidNewOrderStatus = -32,
/// <summary>
/// Exercise time before expiry for European options (-33)
/// </summary>
EuropeanOptionNotExpiredOnExercise = -33,
/// <summary>
/// Option order is invalid due to underlying stock split (-34)
/// </summary>
OptionOrderOnStockSplit = -34,
/// <summary>
/// The Market On Open order was submitted during regular market hours,
/// which is not allowed. This order type must be submitted before the market opens.
/// </summary>
MarketOnOpenNotAllowedDuringRegularHours = -35
}
}
+135
View File
@@ -0,0 +1,135 @@
/*
* 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.Algorithm.Framework.Portfolio;
using QuantConnect.Interfaces;
using QuantConnect.Securities;
using QuantConnect.Securities.Crypto;
namespace QuantConnect.Orders
{
/// <summary>
/// Provides methods for computing a maximum order size.
/// </summary>
public static class OrderSizing
{
/// <summary>
/// Adjust the provided order size to respect maximum order size based on a percentage of current volume.
/// </summary>
/// <param name="security">The security object</param>
/// <param name="maximumPercentCurrentVolume">The maximum percentage of the current bar's volume</param>
/// <param name="desiredOrderSize">The desired order size to adjust</param>
/// <returns>The signed adjusted order size</returns>
public static decimal GetOrderSizeForPercentVolume(Security security, decimal maximumPercentCurrentVolume, decimal desiredOrderSize)
{
var maxOrderSize = maximumPercentCurrentVolume * security.Volume;
var orderSize = Math.Min(maxOrderSize, Math.Abs(desiredOrderSize));
return Math.Sign(desiredOrderSize) * AdjustByLotSize(security, orderSize);
}
/// <summary>
/// Adjust the provided order size to respect the maximum total order value
/// </summary>
/// <param name="security">The security object</param>
/// <param name="maximumOrderValueInAccountCurrency">The maximum order value in units of the account currency</param>
/// <param name="desiredOrderSize">The desired order size to adjust</param>
/// <returns>The signed adjusted order size</returns>
public static decimal GetOrderSizeForMaximumValue(Security security, decimal maximumOrderValueInAccountCurrency, decimal desiredOrderSize)
{
var priceInAccountCurrency = security.Price
* security.QuoteCurrency.ConversionRate
* security.SymbolProperties.ContractMultiplier;
if (priceInAccountCurrency == 0m)
{
return 0m;
}
var maxOrderSize = maximumOrderValueInAccountCurrency / priceInAccountCurrency;
var orderSize = Math.Min(maxOrderSize, Math.Abs(desiredOrderSize));
return Math.Sign(desiredOrderSize) * AdjustByLotSize(security, orderSize);
}
/// <summary>
/// Gets the remaining quantity to be ordered to reach the specified target quantity.
/// </summary>
/// <param name="algorithm">The algorithm instance</param>
/// <param name="target">The portfolio target</param>
/// <returns>The signed remaining quantity to be ordered</returns>
public static decimal GetUnorderedQuantity(IAlgorithm algorithm, IPortfolioTarget target)
{
var security = algorithm.Securities[target.Symbol];
return GetUnorderedQuantity(algorithm, target, security);
}
/// <summary>
/// Gets the remaining quantity to be ordered to reach the specified target quantity.
/// </summary>
/// <param name="algorithm">The algorithm instance</param>
/// <param name="target">The portfolio target</param>
/// <param name="security">The target security</param>
/// <param name="accountForFees">True for taking into account the fee's in the order quantity.
/// False, otherwise.</param>
/// <returns>The signed remaining quantity to be ordered</returns>
public static decimal GetUnorderedQuantity(IAlgorithm algorithm, IPortfolioTarget target, Security security, bool accountForFees = false)
{
var quantity = target.Quantity - algorithm.Transactions.GetProjectedHoldings(security).ProjectedQuantity;
// Adjust the order quantity taking into account the fee's
if (accountForFees && security.Symbol.SecurityType == SecurityType.Crypto && quantity > 0)
{
var orderFee = Extensions.GetMarketOrderFees(security, quantity, algorithm.UtcTime);
var baseCurrency = ((Crypto)security).BaseCurrency.Symbol;
if (baseCurrency == orderFee.Currency)
{
quantity += orderFee.Amount;
}
}
return AdjustByLotSize(security, quantity);
}
/// <summary>
/// Adjusts the provided order quantity to respect the securities lot size.
/// If the quantity is missing 1M part of the lot size it will be rounded up
/// since we suppose it's due to floating point error, this is required to avoid diff
/// between Py and C#
/// </summary>
/// <param name="security">The security instance</param>
/// <param name="quantity">The desired quantity to adjust, can be signed</param>
/// <returns>The signed adjusted quantity</returns>
public static decimal AdjustByLotSize(Security security, decimal quantity)
{
var absQuantity = Math.Abs(quantity);
// if the amount we are missing for +1 lot size is 1M part of a lot size
// we suppose its due to floating point error and round up
// Note: this is required to avoid a diff between Py and C# equivalent
var remainder = absQuantity % security.SymbolProperties.LotSize;
var missingForLotSize = security.SymbolProperties.LotSize - remainder;
if (missingForLotSize < (security.SymbolProperties.LotSize / 1000000))
{
remainder -= security.SymbolProperties.LotSize;
}
absQuantity -= remainder;
return absQuantity * Math.Sign(quantity);
}
}
}
+63
View File
@@ -0,0 +1,63 @@
/*
* 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 Newtonsoft.Json;
namespace QuantConnect.Orders
{
/// <summary>
/// The purpose of this class is to store time and price information
/// available at the time an order was submitted.
/// </summary>
public class OrderSubmissionData
{
/// <summary>
/// The bid price at order submission time
/// </summary>
[JsonProperty(PropertyName = "bidPrice")]
public decimal BidPrice { get; }
/// <summary>
/// The ask price at order submission time
/// </summary>
[JsonProperty(PropertyName = "askPrice")]
public decimal AskPrice { get; }
/// <summary>
/// The current price at order submission time
/// </summary>
[JsonProperty(PropertyName = "lastPrice")]
public decimal LastPrice { get; }
/// <summary>
/// Initializes a new instance of the <see cref="OrderSubmissionData"/> class
/// </summary>
/// <remarks>This method is currently only used for testing.</remarks>
public OrderSubmissionData(decimal bidPrice, decimal askPrice, decimal lastPrice)
{
BidPrice = bidPrice;
AskPrice = askPrice;
LastPrice = lastPrice;
}
/// <summary>
/// Return a new instance clone of this object
/// </summary>
public OrderSubmissionData Clone()
{
return (OrderSubmissionData)MemberwiseClone();
}
}
}
+730
View File
@@ -0,0 +1,730 @@
/*
* 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 System.Threading;
using QuantConnect.Securities;
using static QuantConnect.StringExtensions;
namespace QuantConnect.Orders
{
/// <summary>
/// Provides a single reference to an order for the algorithm to maintain. As the order gets
/// updated this ticket will also get updated
/// </summary>
public sealed class OrderTicket
{
private readonly object _lock = new object();
private Order _order;
private OrderStatus? _orderStatusOverride;
private CancelOrderRequest _cancelRequest;
private FillState _fillState;
private List<OrderEvent> _orderEventsImpl;
private List<UpdateOrderRequest> _updateRequestsImpl;
private readonly SubmitOrderRequest _submitRequest;
private readonly ManualResetEvent _orderStatusClosedEvent;
private readonly ManualResetEvent _orderSetEvent;
// we pull this in to provide some behavior/simplicity to the ticket API
private readonly SecurityTransactionManager _transactionManager;
private List<OrderEvent> _orderEvents { get => _orderEventsImpl ??= new List<OrderEvent>(); }
private List<UpdateOrderRequest> _updateRequests { get => _updateRequestsImpl ??= new List<UpdateOrderRequest>(); }
/// <summary>
/// Gets the order id of this ticket
/// </summary>
public int OrderId
{
get { return _submitRequest.OrderId; }
}
/// <summary>
/// Gets the current status of this order ticket
/// </summary>
public OrderStatus Status
{
get
{
if (_orderStatusOverride.HasValue) return _orderStatusOverride.Value;
return _order == null ? OrderStatus.New : _order.Status;
}
}
/// <summary>
/// Gets the symbol being ordered
/// </summary>
public Symbol Symbol
{
get { return _submitRequest.Symbol; }
}
/// <summary>
/// Gets the <see cref="Symbol"/>'s <see cref="SecurityType"/>
/// </summary>
public SecurityType SecurityType
{
get { return _submitRequest.SecurityType; }
}
/// <summary>
/// Gets the number of units ordered
/// </summary>
public decimal Quantity
{
get { return _order == null ? _submitRequest.Quantity : _order.Quantity; }
}
/// <summary>
/// Gets the average fill price for this ticket. If no fills have been processed
/// then this will return a value of zero.
/// </summary>
public decimal AverageFillPrice
{
get
{
return _fillState.AverageFillPrice;
}
}
/// <summary>
/// Gets the total qantity filled for this ticket. If no fills have been processed
/// then this will return a value of zero.
/// </summary>
public decimal QuantityFilled
{
get
{
return _fillState.QuantityFilled;
}
}
/// <summary>
/// Gets the remaining quantity for this order ticket.
/// This is the difference between the total quantity ordered and the total quantity filled.
/// </summary>
public decimal QuantityRemaining
{
get
{
var currentState = _fillState;
return Quantity - currentState.QuantityFilled;
}
}
/// <summary>
/// Gets the time this order was last updated
/// </summary>
public DateTime Time
{
get { return GetMostRecentOrderRequest().Time; }
}
/// <summary>
/// Gets the type of order
/// </summary>
public OrderType OrderType
{
get { return _submitRequest.OrderType; }
}
/// <summary>
/// Gets the order's current tag
/// </summary>
public string Tag
{
get { return _order == null ? _submitRequest.Tag : _order.Tag; }
}
/// <summary>
/// Gets the <see cref="SubmitOrderRequest"/> that initiated this order
/// </summary>
public SubmitOrderRequest SubmitRequest
{
get { return _submitRequest; }
}
/// <summary>
/// Gets a list of <see cref="UpdateOrderRequest"/> containing an item for each
/// <see cref="UpdateOrderRequest"/> that was sent for this order id
/// </summary>
public IReadOnlyList<UpdateOrderRequest> UpdateRequests
{
get
{
lock (_lock)
{
// Avoid creating the update requests list if not necessary
if (_updateRequestsImpl == null)
{
return Array.Empty<UpdateOrderRequest>();
}
return _updateRequestsImpl.ToList();
}
}
}
/// <summary>
/// Gets the <see cref="CancelOrderRequest"/> if this order was canceled. If this order
/// was not canceled, this will return null
/// </summary>
public CancelOrderRequest CancelRequest
{
get
{
lock (_lock)
{
return _cancelRequest;
}
}
}
/// <summary>
/// Gets a list of all order events for this ticket
/// </summary>
public IReadOnlyList<OrderEvent> OrderEvents
{
get
{
lock (_lock)
{
return _orderEvents.ToList();
}
}
}
/// <summary>
/// Gets a wait handle that can be used to wait until this order has filled
/// </summary>
public WaitHandle OrderClosed
{
get { return _orderStatusClosedEvent; }
}
/// <summary>
/// Returns true if the order has been set for this ticket
/// </summary>
public bool HasOrder => _order != null;
/// <summary>
/// Gets a wait handle that can be used to wait until the order has been set
/// </summary>
public WaitHandle OrderSet => _orderSetEvent;
/// <summary>
/// Initializes a new instance of the <see cref="OrderTicket"/> class
/// </summary>
/// <param name="transactionManager">The transaction manager used for submitting updates and cancels for this ticket</param>
/// <param name="submitRequest">The order request that initiated this order ticket</param>
public OrderTicket(SecurityTransactionManager transactionManager, SubmitOrderRequest submitRequest)
{
_submitRequest = submitRequest;
_transactionManager = transactionManager;
_orderStatusClosedEvent = new ManualResetEvent(false);
_orderSetEvent = new ManualResetEvent(false);
_fillState = new FillState(0m, 0m);
}
/// <summary>
/// Gets the specified field from the ticket
/// </summary>
/// <param name="field">The order field to get</param>
/// <returns>The value of the field</returns>
/// <exception cref="ArgumentException">Field out of range</exception>
/// <exception cref="ArgumentOutOfRangeException">Field out of range for order type</exception>
public decimal Get(OrderField field)
{
return Get<decimal>(field);
}
/// <summary>
/// Gets the specified field from the ticket and tries to convert it to the specified type
/// </summary>
/// <param name="field">The order field to get</param>
/// <returns>The value of the field</returns>
/// <exception cref="ArgumentException">Field out of range</exception>
/// <exception cref="ArgumentOutOfRangeException">Field out of range for order type</exception>
public T Get<T>(OrderField field)
{
object fieldValue = null;
switch (field)
{
case OrderField.LimitPrice:
if (_submitRequest.OrderType == OrderType.ComboLimit)
{
fieldValue = AccessOrder<ComboLimitOrder, decimal>(this, field, o => o.GroupOrderManager.LimitPrice, r => r.LimitPrice);
}
else if (_submitRequest.OrderType == OrderType.ComboLegLimit)
{
fieldValue = AccessOrder<ComboLegLimitOrder, decimal>(this, field, o => o.LimitPrice, r => r.LimitPrice);
}
else if (_submitRequest.OrderType == OrderType.Limit)
{
fieldValue = AccessOrder<LimitOrder, decimal>(this, field, o => o.LimitPrice, r => r.LimitPrice);
}
else if (_submitRequest.OrderType == OrderType.StopLimit)
{
fieldValue = AccessOrder<StopLimitOrder, decimal>(this, field, o => o.LimitPrice, r => r.LimitPrice);
}
else if (_submitRequest.OrderType == OrderType.LimitIfTouched)
{
fieldValue = AccessOrder<LimitIfTouchedOrder, decimal>(this, field, o => o.LimitPrice, r => r.LimitPrice);
}
break;
case OrderField.StopPrice:
if (_submitRequest.OrderType == OrderType.StopLimit)
{
fieldValue = AccessOrder<StopLimitOrder, decimal>(this, field, o => o.StopPrice, r => r.StopPrice);
}
else if (_submitRequest.OrderType == OrderType.StopMarket)
{
fieldValue = AccessOrder<StopMarketOrder, decimal>(this, field, o => o.StopPrice, r => r.StopPrice);
}
else if (_submitRequest.OrderType == OrderType.TrailingStop)
{
fieldValue = AccessOrder<TrailingStopOrder, decimal>(this, field, o => o.StopPrice, r => r.StopPrice);
}
break;
case OrderField.TriggerPrice:
fieldValue = AccessOrder<LimitIfTouchedOrder, decimal>(this, field, o => o.TriggerPrice, r => r.TriggerPrice);
break;
case OrderField.TrailingAmount:
fieldValue = AccessOrder<TrailingStopOrder, decimal>(this, field, o => o.TrailingAmount, r => r.TrailingAmount);
break;
case OrderField.TrailingAsPercentage:
fieldValue = AccessOrder<TrailingStopOrder, bool>(this, field, o => o.TrailingAsPercentage, r => r.TrailingAsPercentage);
break;
default:
throw new ArgumentOutOfRangeException(nameof(field), field, null);
}
if (fieldValue == null)
{
throw new ArgumentException(Messages.OrderTicket.GetFieldError(this, field));
}
return (T)fieldValue;
}
/// <summary>
/// Submits an <see cref="UpdateOrderRequest"/> with the <see cref="SecurityTransactionManager"/> to update
/// the ticket with data specified in <paramref name="fields"/>
/// </summary>
/// <param name="fields">Defines what properties of the order should be updated</param>
/// <returns>The <see cref="OrderResponse"/> from updating the order</returns>
public OrderResponse Update(UpdateOrderFields fields)
{
var ticket = _transactionManager.UpdateOrder(new UpdateOrderRequest(_transactionManager.UtcTime, SubmitRequest.OrderId, fields));
return ticket.UpdateRequests.Last().Response;
}
/// <summary>
/// Submits an <see cref="UpdateOrderRequest"/> with the <see cref="SecurityTransactionManager"/> to update
/// the ticket with tag specified in <paramref name="tag"/>
/// </summary>
/// <param name="tag">The new tag for this order ticket</param>
/// <returns><see cref="OrderResponse"/> from updating the order</returns>
public OrderResponse UpdateTag(string tag)
{
var fields = new UpdateOrderFields()
{
Tag = tag
};
return Update(fields);
}
/// <summary>
/// Submits an <see cref="UpdateOrderRequest"/> with the <see cref="SecurityTransactionManager"/> to update
/// the ticket with quantity specified in <paramref name="quantity"/> and with tag specified in <paramref name="quantity"/>
/// </summary>
/// <param name="quantity">The new quantity for this order ticket</param>
/// <param name="tag">The new tag for this order ticket</param>
/// <returns><see cref="OrderResponse"/> from updating the order</returns>
public OrderResponse UpdateQuantity(decimal quantity, string tag = null)
{
var fields = new UpdateOrderFields()
{
Quantity = quantity,
Tag = tag
};
return Update(fields);
}
/// <summary>
/// Submits an <see cref="UpdateOrderRequest"/> with the <see cref="SecurityTransactionManager"/> to update
/// the ticker with limit price specified in <paramref name="limitPrice"/> and with tag specified in <paramref name="tag"/>
/// </summary>
/// <param name="limitPrice">The new limit price for this order ticket</param>
/// <param name="tag">The new tag for this order ticket</param>
/// <returns><see cref="OrderResponse"/> from updating the order</returns>
public OrderResponse UpdateLimitPrice(decimal limitPrice, string tag = null)
{
var fields = new UpdateOrderFields()
{
LimitPrice = limitPrice,
Tag = tag
};
return Update(fields);
}
/// <summary>
/// Submits an <see cref="UpdateOrderRequest"/> with the <see cref="SecurityTransactionManager"/> to update
/// the ticker with stop price specified in <paramref name="stopPrice"/> and with tag specified in <paramref name="tag"/>
/// </summary>
/// <param name="stopPrice">The new stop price for this order ticket</param>
/// <param name="tag">The new tag for this order ticket</param>
/// <returns><see cref="OrderResponse"/> from updating the order</returns>
public OrderResponse UpdateStopPrice(decimal stopPrice, string tag = null)
{
var fields = new UpdateOrderFields()
{
StopPrice = stopPrice,
Tag = tag
};
return Update(fields);
}
/// <summary>
/// Submits an <see cref="UpdateOrderRequest"/> with the <see cref="SecurityTransactionManager"/> to update
/// the ticker with trigger price specified in <paramref name="triggerPrice"/> and with tag specified in <paramref name="tag"/>
/// </summary>
/// <param name="triggerPrice">The new price which, when touched, will trigger the setting of a limit order.</param>
/// <param name="tag">The new tag for this order ticket</param>
/// <returns><see cref="OrderResponse"/> from updating the order</returns>
public OrderResponse UpdateTriggerPrice(decimal triggerPrice, string tag = null)
{
var fields = new UpdateOrderFields()
{
TriggerPrice = triggerPrice,
Tag = tag
};
return Update(fields);
}
/// <summary>
/// Submits an <see cref="UpdateOrderRequest"/> with the <see cref="SecurityTransactionManager"/> to update
/// the ticker with stop trailing amount specified in <paramref name="trailingAmount"/> and with tag specified in <paramref name="tag"/>
/// </summary>
/// <param name="trailingAmount">The new trailing amount for this order ticket</param>
/// <param name="tag">The new tag for this order ticket</param>
/// <returns><see cref="OrderResponse"/> from updating the order</returns>
public OrderResponse UpdateStopTrailingAmount(decimal trailingAmount, string tag = null)
{
var fields = new UpdateOrderFields()
{
TrailingAmount = trailingAmount,
Tag = tag
};
return Update(fields);
}
/// <summary>
/// Submits a new request to cancel this order
/// </summary>
public OrderResponse Cancel(string tag = null)
{
var request = new CancelOrderRequest(_transactionManager.UtcTime, OrderId, tag);
lock (_lock)
{
// don't submit duplicate cancel requests, if the cancel request wasn't flagged as error
// this could happen when trying to cancel an order which status is still new and hasn't even been submitted to the brokerage
if (_cancelRequest != null && _cancelRequest.Status != OrderRequestStatus.Error)
{
return OrderResponse.Error(request, OrderResponseErrorCode.RequestCanceled,
Messages.OrderTicket.CancelRequestAlreadySubmitted(this));
}
}
var ticket = _transactionManager.ProcessRequest(request);
return ticket.CancelRequest.Response;
}
/// <summary>
/// Gets the most recent <see cref="OrderResponse"/> for this ticket
/// </summary>
/// <returns>The most recent <see cref="OrderResponse"/> for this ticket</returns>
public OrderResponse GetMostRecentOrderResponse()
{
return GetMostRecentOrderRequest().Response;
}
/// <summary>
/// Gets the most recent <see cref="OrderRequest"/> for this ticket
/// </summary>
/// <returns>The most recent <see cref="OrderRequest"/> for this ticket</returns>
public OrderRequest GetMostRecentOrderRequest()
{
lock (_lock)
{
if (_cancelRequest != null)
{
return _cancelRequest;
}
// Avoid creating the update requests list if not necessary
if (_updateRequestsImpl != null)
{
var lastUpdate = _updateRequestsImpl.LastOrDefault();
if (lastUpdate != null)
{
return lastUpdate;
}
}
}
return SubmitRequest;
}
/// <summary>
/// Adds an order event to this ticket
/// </summary>
/// <param name="orderEvent">The order event to be added</param>
internal void AddOrderEvent(OrderEvent orderEvent)
{
lock (_lock)
{
_orderEvents.Add(orderEvent);
// Update the ticket and order
if (orderEvent.FillQuantity != 0)
{
var filledQuantity = _fillState.QuantityFilled;
var averageFillPrice = _fillState.AverageFillPrice;
if (_order.Type != OrderType.OptionExercise)
{
// keep running totals of quantity filled and the average fill price so we
// don't need to compute these on demand
filledQuantity += orderEvent.FillQuantity;
var quantityWeightedFillPrice = _orderEvents.Where(x => x.Status.IsFill())
.Aggregate(0m, (d, x) => d + x.AbsoluteFillQuantity * x.FillPrice);
averageFillPrice = quantityWeightedFillPrice / Math.Abs(filledQuantity);
_order.Price = averageFillPrice;
}
// For ITM option exercise orders we set the order price to the strike price.
// For OTM the fill price should be zero, which is the default for OptionExerciseOrders
else if (orderEvent.IsInTheMoney)
{
_order.Price = Symbol.ID.StrikePrice;
// We update the ticket only if the fill price is not zero (this fixes issue #2846 where average price
// is skewed by the removal of the option).
if (orderEvent.FillPrice != 0)
{
filledQuantity += orderEvent.FillQuantity;
averageFillPrice = _order.Price;
}
}
_fillState = new FillState(averageFillPrice, filledQuantity);
}
}
// fire the wait handle indicating this order is closed
if (orderEvent.Status.IsClosed())
{
_orderStatusClosedEvent.Set();
}
}
/// <summary>
/// Updates the internal order object with the current state
/// </summary>
/// <param name="order">The order</param>
internal void SetOrder(Order order)
{
if (_order != null && _order.Id != order.Id)
{
throw new ArgumentException("Order id mismatch");
}
_order = order;
_orderSetEvent.Set();
}
/// <summary>
/// Adds a new <see cref="UpdateOrderRequest"/> to this ticket.
/// </summary>
/// <param name="request">The recently processed <see cref="UpdateOrderRequest"/></param>
internal void AddUpdateRequest(UpdateOrderRequest request)
{
if (request.OrderId != OrderId)
{
throw new ArgumentException("Received UpdateOrderRequest for incorrect order id.");
}
lock (_lock)
{
_updateRequests.Add(request);
}
}
/// <summary>
/// Sets the <see cref="CancelOrderRequest"/> for this ticket. This can only be performed once.
/// </summary>
/// <remarks>
/// This method is thread safe.
/// </remarks>
/// <param name="request">The <see cref="CancelOrderRequest"/> that canceled this ticket.</param>
/// <returns>False if the the CancelRequest has already been set, true if this call set it</returns>
internal bool TrySetCancelRequest(CancelOrderRequest request)
{
if (request.OrderId != OrderId)
{
throw new ArgumentException("Received CancelOrderRequest for incorrect order id.");
}
lock (_lock)
{
// don't submit duplicate cancel requests, if the cancel request wasn't flagged as error
// this could happen when trying to cancel an order which status is still new and hasn't even been submitted to the brokerage
if (_cancelRequest != null && _cancelRequest.Status != OrderRequestStatus.Error)
{
return false;
}
_cancelRequest = request;
}
return true;
}
/// <summary>
/// Creates a new <see cref="OrderTicket"/> that represents trying to cancel an order for which no ticket exists
/// </summary>
public static OrderTicket InvalidCancelOrderId(SecurityTransactionManager transactionManager, CancelOrderRequest request)
{
var submit = new SubmitOrderRequest(OrderType.Market, SecurityType.Base, Symbol.Empty, 0, 0, 0, DateTime.MaxValue, request.Tag);
submit.SetResponse(OrderResponse.UnableToFindOrder(request));
submit.SetOrderId(request.OrderId);
var ticket = new OrderTicket(transactionManager, submit);
request.SetResponse(OrderResponse.UnableToFindOrder(request));
ticket.TrySetCancelRequest(request);
ticket._orderStatusOverride = OrderStatus.Invalid;
return ticket;
}
/// <summary>
/// Creates a new <see cref="OrderTicket"/> that represents trying to update an order for which no ticket exists
/// </summary>
public static OrderTicket InvalidUpdateOrderId(SecurityTransactionManager transactionManager, UpdateOrderRequest request)
{
var submit = new SubmitOrderRequest(OrderType.Market, SecurityType.Base, Symbol.Empty, 0, 0, 0, DateTime.MaxValue, request.Tag);
submit.SetResponse(OrderResponse.UnableToFindOrder(request));
submit.SetOrderId(request.OrderId);
var ticket = new OrderTicket(transactionManager, submit);
request.SetResponse(OrderResponse.UnableToFindOrder(request));
ticket.AddUpdateRequest(request);
ticket._orderStatusOverride = OrderStatus.Invalid;
return ticket;
}
/// <summary>
/// Creates a new <see cref="OrderTicket"/> that represents trying to submit a new order that had errors embodied in the <paramref name="response"/>
/// </summary>
public static OrderTicket InvalidSubmitRequest(SecurityTransactionManager transactionManager, SubmitOrderRequest request, OrderResponse response)
{
request.SetResponse(response);
return new OrderTicket(transactionManager, request) { _orderStatusOverride = OrderStatus.Invalid };
}
/// <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()
{
var requestCount = 1;
var responseCount = _submitRequest.Response == OrderResponse.Unprocessed ? 0 : 1;
lock (_lock)
{
// Avoid creating the update requests list if not necessary
if (_updateRequestsImpl != null)
{
requestCount += _updateRequestsImpl.Count;
responseCount += _updateRequestsImpl.Count(x => x.Response != OrderResponse.Unprocessed);
}
requestCount += _cancelRequest == null ? 0 : 1;
responseCount += _cancelRequest == null || _cancelRequest.Response == OrderResponse.Unprocessed ? 0 : 1;
}
return Messages.OrderTicket.ToString(this, _order, requestCount, responseCount);
}
/// <summary>
/// This is provided for API backward compatibility and will resolve to the order ID, except during
/// an error, where it will return the integer value of the <see cref="OrderResponseErrorCode"/> from
/// the most recent response
/// </summary>
public static implicit operator int(OrderTicket ticket)
{
var response = ticket.GetMostRecentOrderResponse();
if (response != null && response.IsError)
{
return (int) response.ErrorCode;
}
return ticket.OrderId;
}
private static P AccessOrder<T, P>(OrderTicket ticket, OrderField field, Func<T, P> orderSelector, Func<SubmitOrderRequest, P> requestSelector)
where T : Order
{
var order = ticket._order;
if (order == null)
{
return requestSelector(ticket._submitRequest);
}
var typedOrder = order as T;
if (typedOrder != null)
{
return orderSelector(typedOrder);
}
throw new ArgumentException(Invariant($"Unable to access property {field} on order of type {order.Type}"));
}
/// <summary>
/// Reference wrapper for decimal average fill price and quantity filled.
/// In order to update the average fill price and quantity filled, we create a new instance of this class
/// so we avoid potential race conditions when accessing these properties
/// (e.g. the decimals might be being updated and in a invalid state when being read)
/// </summary>
private class FillState
{
public decimal AverageFillPrice { get; }
public decimal QuantityFilled { get; }
public FillState(decimal averageFillPrice, decimal quantityFilled)
{
AverageFillPrice = averageFillPrice;
QuantityFilled = quantityFilled;
}
}
}
}
+185
View File
@@ -0,0 +1,185 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Type of the order: market, limit or stop
/// </summary>
public enum OrderType
{
/// <summary>
/// Market Order Type (0)
/// </summary>
Market,
/// <summary>
/// Limit Order Type (1)
/// </summary>
Limit,
/// <summary>
/// Stop Market Order Type - Fill at market price when break target price (2)
/// </summary>
StopMarket,
/// <summary>
/// Stop limit order type - trigger fill once pass the stop price; but limit fill to limit price (3)
/// </summary>
StopLimit,
/// <summary>
/// Market on open type - executed on exchange open (4)
/// </summary>
MarketOnOpen,
/// <summary>
/// Market on close type - executed on exchange close (5)
/// </summary>
MarketOnClose,
/// <summary>
/// Option Exercise Order Type (6)
/// </summary>
OptionExercise,
/// <summary>
/// Limit if Touched Order Type - a limit order to be placed after first reaching a trigger value (7)
/// </summary>
LimitIfTouched,
/// <summary>
/// Combo Market Order Type - (8)
/// </summary>
ComboMarket,
/// <summary>
/// Combo Limit Order Type - (9)
/// </summary>
ComboLimit,
/// <summary>
/// Combo Leg Limit Order Type - (10)
/// </summary>
ComboLegLimit,
/// <summary>
/// Trailing Stop Order Type - (11)
/// </summary>
TrailingStop
}
/// <summary>
/// Direction of the order
/// </summary>
public enum OrderDirection
{
/// <summary>
/// Buy Order (0)
/// </summary>
Buy,
/// <summary>
/// Sell Order (1)
/// </summary>
Sell,
/// <summary>
/// Default Value - No Order Direction (2)
/// </summary>
/// <remarks>
/// Unfortunately this does not have a value of zero because
/// there are backtests saved that reference the values in this order
/// </remarks>
Hold
}
/// <summary>
/// Position of the order
/// </summary>
public enum OrderPosition
{
/// <summary>
/// Indicates the buy order will result in a long position, starting either from zero or an existing long position (0)
/// </summary>
BuyToOpen,
/// <summary>
/// Indicates the buy order is starting from an existing short position, resulting in a closed or long position (1)
/// </summary>
BuyToClose,
/// <summary>
/// Indicates the sell order will result in a short position, starting either from zero or an existing short position (2)
/// </summary>
SellToOpen,
/// <summary>
/// Indicates the sell order is starting from an existing long position, resulting in a closed or short position (3)
/// </summary>
SellToClose,
}
/// <summary>
/// Fill status of the order class.
/// </summary>
public enum OrderStatus
{
/// <summary>
/// New order pre-submission to the order processor (0)
/// </summary>
New = 0,
/// <summary>
/// Order submitted to the market (1)
/// </summary>
Submitted = 1,
/// <summary>
/// Partially filled, In Market Order (2)
/// </summary>
PartiallyFilled = 2,
/// <summary>
/// Completed, Filled, In Market Order (3)
/// </summary>
Filled = 3,
/// <summary>
/// Order cancelled before it was filled (5)
/// </summary>
Canceled = 5,
/// <summary>
/// No Order State Yet (6)
/// </summary>
None = 6,
/// <summary>
/// Order invalidated before it hit the market (e.g. insufficient capital) (7)
/// </summary>
Invalid = 7,
/// <summary>
/// Order waiting for confirmation of cancellation (8)
/// </summary>
CancelPending = 8,
/// <summary>
/// Order update submitted to the market (9)
/// </summary>
UpdateSubmitted = 9
}
}
+39
View File
@@ -0,0 +1,39 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Event that fires each time an order is updated in the brokerage side.
/// These are not status changes but mainly price changes, like the stop price of a trailing stop order.
/// </summary>
public class OrderUpdateEvent
{
/// <summary>
/// The order ID.
/// </summary>
public int OrderId { get; set; }
/// <summary>
/// The updated stop price for a <see cref="TrailingStopOrder"/>
/// </summary>
public decimal TrailingStopPrice { get; set; }
/// <summary>
/// Flag indicating whether stop has been triggered for a <see cref="StopLimitOrder"/>
/// </summary>
public bool StopTriggered { get; set; }
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
* 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 Newtonsoft.Json;
using QuantConnect.Api;
using System.Collections.Generic;
using QuantConnect.Orders.Serialization;
namespace QuantConnect.Orders
{
/// <summary>
/// Collection container for a list of orders for a project
/// </summary>
public class OrdersResponseWrapper : RestResponse
{
/// <summary>
/// Returns the total order collection length, not only the amount we are sending here
/// </summary>
[JsonProperty(PropertyName = "length")]
public int Length { get; set; }
/// <summary>
/// Collection of summarized Orders objects
/// </summary>
[JsonProperty(PropertyName = "orders")]
public List<ApiOrderResponse> Orders { get; set; } = new();
}
/// <summary>
/// Api order and order events reponse
/// </summary>
[JsonConverter(typeof(ReadOrdersResponseJsonConverter))]
public class ApiOrderResponse: StringRepresentation
{
/// <summary>
/// The symbol associated with this order
/// </summary>
public Symbol Symbol { get; set; }
/// <summary>
/// The order
/// </summary>
public Order Order { get; set; }
/// <summary>
/// The order events
/// </summary>
public List<SerializedOrderEvent> Events { get; set; }
/// <summary>
/// ApiOrderResponse empty constructor
/// </summary>
public ApiOrderResponse()
{
}
/// <summary>
/// Creates an instance of an ApiOrderResponse class using the given arguments
/// </summary>
public ApiOrderResponse(Order order, List<SerializedOrderEvent> events, Symbol symbol)
{
Order = order;
Events = events;
Symbol = symbol;
}
}
}
+40
View File
@@ -0,0 +1,40 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace QuantConnect.Orders
{
/// <summary>
/// Represents the properties of an order in Public.com.
/// </summary>
public class PublicOrderProperties : OrderProperties
{
/// <summary>
/// If set to <c>true</c>, allows the order to trigger or fill outside of regular trading hours
/// (the extended session).
/// </summary>
/// <remarks>
/// Applicable to day time-in-force equity orders only.
/// </remarks>
public bool OutsideRegularTradingHours { get; set; }
/// <summary>
/// Controls the buying power used by the order.
/// <c>true</c> uses margin buying power when the account allows it; <c>false</c> uses cash-only buying power.
/// When left <c>null</c>, the brokerage model fills it in from the account type before the order is sent.
/// </summary>
public bool? UseMargin { get; set; }
}
}
+25
View File
@@ -0,0 +1,25 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
namespace QuantConnect.Orders
{
/// <summary>
/// RBI order properties
/// </summary>
public class RBIOrderProperties : OrderProperties
{
}
}
@@ -0,0 +1,76 @@
/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2024 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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using QuantConnect.Orders.Serialization;
namespace QuantConnect.Orders
{
/// <summary>
/// Api orders read response json converter
/// </summary>
public class ReadOrdersResponseJsonConverter : JsonConverter
{
/// <summary>
/// Determines if can convert the given open type
/// </summary>
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ApiOrderResponse);
}
/// <summary>
/// Serialize the given api order response
/// </summary>
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var orderResponse = (ApiOrderResponse)value;
var jObject = JObject.FromObject(orderResponse.Order);
jObject["symbol"] = JToken.FromObject(orderResponse.Symbol);
jObject["events"] = JToken.FromObject(orderResponse.Events);
jObject.WriteTo(writer);
}
/// <summary>
/// Deserialize the given api order response
/// </summary>
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var jObject = JObject.Load(reader);
serializer.Converters.Add(new OrderJsonConverter());
var order = jObject.ToObject<Order>(serializer);
var events = jObject["Events"] ?? jObject["events"];
List<SerializedOrderEvent> deserializedEvents = null;
if (events != null)
{
deserializedEvents = events.ToObject<List<SerializedOrderEvent>>();
}
var symbol = jObject["Symbol"] ?? jObject["symbol"];
Symbol deserializedSymbol = null;
if (symbol != null)
{
deserializedSymbol = symbol.ToObject<Symbol>();
}
return new ApiOrderResponse(order, deserializedEvents ?? new(), deserializedSymbol);
}
}
}

Some files were not shown because too many files have changed in this diff Show More