/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ using System; using System.Linq; using QuantConnect.Orders; using QuantConnect.Interfaces; using System.Collections.Generic; using QuantConnect.Securities.Option; using QuantConnect.Securities.Positions; namespace QuantConnect.Algorithm.CSharp { /// /// Base class for equity option strategy regression algorithms which holds some basic shared setup logic /// public abstract class OptionEquityBaseStrategyRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition { protected decimal _paidFees { get; set; } protected Symbol _optionSymbol { get; set; } public override void Initialize() { SetStartDate(2015, 12, 24); SetEndDate(2015, 12, 24); SetCash(200000); var equity = AddEquity("GOOG", leverage: 4); var option = AddOption(equity.Symbol); _optionSymbol = option.Symbol; // set our strike/expiry filter for this option chain option.SetFilter(u => u.Strikes(-2, +2) // Expiration method accepts TimeSpan objects or integer for days. // The following statements yield the same filtering criteria .Expiration(0, 180)); } protected void AssertOptionStrategyIsPresent(string name, int? quantity = null) { if (Portfolio.Positions.Groups.Where(group => group.BuyingPowerModel is OptionStrategyPositionGroupBuyingPowerModel) .Count(group => ((OptionStrategyPositionGroupBuyingPowerModel)@group.BuyingPowerModel).ToString() == name && (!quantity.HasValue || Math.Abs(group.Quantity) == quantity)) != 1) { throw new RegressionTestException($"Option strategy: '{name}' was not found!"); } } protected void AssertDefaultGroup(Symbol symbol, decimal quantity) { if (Portfolio.Positions.Groups.Where(group => group.BuyingPowerModel is SecurityPositionGroupBuyingPowerModel) .Count(group => group.Positions.Any(position => position.Symbol == symbol && position.Quantity == quantity)) != 1) { throw new RegressionTestException($"Default groupd for symbol '{symbol}' and quantity '{quantity}' was not found!"); } } protected decimal GetPriceSpreadDifference(params Symbol[] symbols) { var spreadPaid = 0m; foreach (var symbol in symbols) { var security = Securities[symbol]; var actualQuantity = security.Holdings.AbsoluteQuantity; var spread = 0m; if (security.Holdings.IsLong) { if (security.AskPrice != 0) { spread = security.Price - security.AskPrice; } } else if(security.BidPrice != 0) { spread = security.BidPrice - security.Price; } spreadPaid += spread * actualQuantity * security.SymbolProperties.ContractMultiplier; } return spreadPaid; } /// /// Order fill event handler. On an order fill update the resulting information is passed to this method. /// /// Order event details containing details of the events /// This method can be called asynchronously and so should only be used by seasoned C# experts. Ensure you use proper locks on thread-unsafe objects public override void OnOrderEvent(OrderEvent orderEvent) { if (orderEvent.Status == OrderStatus.Filled) { _paidFees += orderEvent.OrderFee.Value.Amount; if (orderEvent.Symbol.SecurityType.IsOption()) { var security = Securities[orderEvent.Symbol]; var premiumPaid = orderEvent.Quantity * orderEvent.FillPrice * security.SymbolProperties.ContractMultiplier; Log($"{orderEvent}. Premium paid: {premiumPaid}"); return; } } Log($"{orderEvent}"); } /// /// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm. /// public bool CanRunLocally => true; /// /// This is used by the regression test system to indicate which languages this algorithm is written in. /// public List Languages { get; } = new() { Language.CSharp }; /// /// Data Points count of all timeslices of algorithm /// public virtual long DataPoints => 0; /// /// Data Points count of the algorithm history /// public virtual int AlgorithmHistoryDataPoints => 0; /// /// Final status of the algorithm /// public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed; /// /// This is used by the regression test system to indicate what the expected statistics are from running the algorithm /// public abstract Dictionary ExpectedStatistics { get; } } }