chore: import upstream snapshot with attribution
This commit is contained in:
+88
@@ -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 System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Stub class providing an idea towards an optimal <see cref="IOptionPositionCollectionEnumerator"/> implementation
|
||||
/// that still needs to be implemented.
|
||||
/// </summary>
|
||||
public class AbsoluteRiskOptionPositionCollectionEnumerator : IOptionPositionCollectionEnumerator
|
||||
{
|
||||
private readonly Func<Symbol, decimal> _marketPriceProvider;
|
||||
|
||||
/// <summary>
|
||||
/// Intializes a new instance of the <see cref="AbsoluteRiskOptionPositionCollectionEnumerator"/> class
|
||||
/// </summary>
|
||||
/// <param name="marketPriceProvider">Function providing the current market price for a provided symbol</param>
|
||||
public AbsoluteRiskOptionPositionCollectionEnumerator(Func<Symbol, decimal> marketPriceProvider)
|
||||
{
|
||||
_marketPriceProvider = marketPriceProvider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the provided <paramref name="positions"/>. Positions enumerated first are more
|
||||
/// likely to be matched than those appearing later in the enumeration.
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> Enumerate(OptionPositionCollection positions)
|
||||
{
|
||||
if (positions.IsEmpty)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
|
||||
var marketPrice = _marketPriceProvider(positions.Underlying);
|
||||
|
||||
var longPositions = new List<OptionPosition>();
|
||||
var shortPuts = new SortedDictionary<decimal, OptionPosition>();
|
||||
var shortCalls = new SortedDictionary<decimal, OptionPosition>();
|
||||
foreach (var position in positions)
|
||||
{
|
||||
if (!position.Symbol.HasUnderlying)
|
||||
{
|
||||
yield return position;
|
||||
}
|
||||
|
||||
if (position.Quantity > 0)
|
||||
{
|
||||
longPositions.Add(position);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (position.Right)
|
||||
{
|
||||
case OptionRight.Put:
|
||||
shortPuts.Add(position.Strike, position);
|
||||
break;
|
||||
|
||||
case OptionRight.Call:
|
||||
shortCalls.Add(position.Strike, position);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ApplicationException(
|
||||
"The skies are falling, the oceans rising - you're having a bad time"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new NotImplementedException("This implementation needs to be completed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
+82
@@ -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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an implementation of <see cref="IOptionStrategyLegPredicateReferenceValue"/> that represents a constant value.
|
||||
/// </summary>
|
||||
public class ConstantOptionStrategyLegPredicateReferenceValue<T> : IOptionStrategyLegPredicateReferenceValue
|
||||
{
|
||||
private readonly T _value;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of this value
|
||||
/// </summary>
|
||||
public PredicateTargetValue Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConstantOptionStrategyLegPredicateReferenceValue{T}"/> class
|
||||
/// </summary>
|
||||
/// <param name="value">The constant reference value</param>
|
||||
/// <param name="target">The value target in relation to the <see cref="OptionPosition"/></param>
|
||||
public ConstantOptionStrategyLegPredicateReferenceValue(T value, PredicateTargetValue target)
|
||||
{
|
||||
_value = value;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the constant value provided at initialization
|
||||
/// </summary>
|
||||
public object Resolve(IReadOnlyList<OptionPosition> legs)
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides methods for easily creating instances of <see cref="ConstantOptionStrategyLegPredicateReferenceValue{T}"/>
|
||||
/// </summary>
|
||||
public static class ConstantOptionStrategyLegReferenceValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ConstantOptionStrategyLegPredicateReferenceValue{T}"/> class for
|
||||
/// the specified <paramref name="value"/>
|
||||
/// </summary>
|
||||
public static IOptionStrategyLegPredicateReferenceValue Create(object value)
|
||||
{
|
||||
if (value is DateTime)
|
||||
{
|
||||
return new ConstantOptionStrategyLegPredicateReferenceValue<DateTime>((DateTime) value, PredicateTargetValue.Expiration);
|
||||
}
|
||||
|
||||
if (value is decimal)
|
||||
{
|
||||
return new ConstantOptionStrategyLegPredicateReferenceValue<decimal>((decimal) value, PredicateTargetValue.Strike);
|
||||
}
|
||||
|
||||
if (value is OptionRight)
|
||||
{
|
||||
return new ConstantOptionStrategyLegPredicateReferenceValue<OptionRight>((OptionRight) value, PredicateTargetValue.Right);
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"{value?.GetType().GetBetterTypeName()} is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a default implementation of the <see cref="IOptionPositionCollectionEnumerator"/> abstraction.
|
||||
/// </summary>
|
||||
public class DefaultOptionPositionCollectionEnumerator : IOptionPositionCollectionEnumerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates <paramref name="positions"/> according to its default enumerator implementation.
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> Enumerate(OptionPositionCollection positions)
|
||||
{
|
||||
return positions;
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an implementation of <see cref="IOptionStrategyDefinitionEnumerator"/> that enumerates definitions
|
||||
/// requiring more leg matches first. This ensures more complex definitions are evaluated before simpler definitions.
|
||||
/// </summary>
|
||||
public class DescendingByLegCountOptionStrategyDefinitionEnumerator : IOptionStrategyDefinitionEnumerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates definitions in descending order of <see cref="OptionStrategyDefinition.LegCount"/>
|
||||
/// </summary>
|
||||
public IEnumerable<OptionStrategyDefinition> Enumerate(IReadOnlyList<OptionStrategyDefinition> definitions)
|
||||
{
|
||||
return definitions.OrderByDescending(d => d.LegCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
+49
@@ -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.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a functional implementation of <see cref="IOptionPositionCollectionEnumerator"/>
|
||||
/// </summary>
|
||||
public class FunctionalOptionPositionCollectionEnumerator : IOptionPositionCollectionEnumerator
|
||||
{
|
||||
private readonly Func<OptionPositionCollection, IEnumerable<OptionPosition>> _enumerate;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FunctionalOptionPositionCollectionEnumerator"/> class
|
||||
/// </summary>
|
||||
/// <param name="enumerate"></param>
|
||||
public FunctionalOptionPositionCollectionEnumerator(
|
||||
Func<OptionPositionCollection, IEnumerable<OptionPosition>> enumerate
|
||||
)
|
||||
{
|
||||
_enumerate = enumerate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerate the Option Positions Collection
|
||||
/// </summary>
|
||||
/// <param name="positions">The positions to enumerate on</param>
|
||||
/// <returns>Enumerable of Option Positions</returns>
|
||||
public IEnumerable<OptionPosition> Enumerate(OptionPositionCollection positions)
|
||||
{
|
||||
return _enumerate(positions);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates an <see cref="OptionPositionCollection"/>. The intent is to evaluate positions that
|
||||
/// may be more important sooner. Positions appearing earlier in the enumeration are evaluated before
|
||||
/// positions showing later. This effectively prioritizes individual positions. This should not be
|
||||
/// used filter filtering, but it could also be used to split a position, for example a position with
|
||||
/// 10 could be changed to two 5s and they don't need to be enumerated back to-back either. In this
|
||||
/// way you could prioritize the first 5 and then delay matching of the final 5.
|
||||
/// </summary>
|
||||
public interface IOptionPositionCollectionEnumerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the provided <paramref name="positions"/>. Positions enumerated first are more
|
||||
/// likely to be matched than those appearing later in the enumeration.
|
||||
/// </summary>
|
||||
IEnumerable<OptionPosition> Enumerate(OptionPositionCollection positions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates <see cref="OptionStrategyDefinition"/> for the purposes of providing a bias towards definitions
|
||||
/// that are more favorable to be matched before matching less favorable definitions.
|
||||
/// </summary>
|
||||
public interface IOptionStrategyDefinitionEnumerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the <paramref name="definitions"/> according to the implementation's own concept of favorability.
|
||||
/// </summary>
|
||||
IEnumerable<OptionStrategyDefinition> Enumerate(IReadOnlyList<OptionStrategyDefinition> definitions);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// When decoding leg predicates, we extract the value we're comparing against
|
||||
/// If we're comparing against another leg's value (such as legs[0].Strike), then
|
||||
/// we'll create a OptionStrategyLegPredicateReferenceValue. If we're comparing against a literal/constant value,
|
||||
/// then we'll create a ConstantOptionStrategyLegPredicateReferenceValue. These reference values are used to slice
|
||||
/// the <see cref="OptionPositionCollection"/> to only include positions matching the
|
||||
/// predicate.
|
||||
/// </summary>
|
||||
public interface IOptionStrategyLegPredicateReferenceValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the target of this value
|
||||
/// </summary>
|
||||
PredicateTargetValue Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the value of the comparand specified in an <see cref="OptionStrategyLegPredicate"/>.
|
||||
/// For example, the predicate may include ... > legs[0].Strike, and upon evaluation, we need to
|
||||
/// be able to extract leg[0].Strike for the currently contemplated set of legs adhering to a
|
||||
/// strategy's definition.
|
||||
/// </summary>
|
||||
object Resolve(IReadOnlyList<OptionPosition> legs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluates the provided match to assign an objective score. Higher scores are better.
|
||||
/// </summary>
|
||||
public interface IOptionStrategyMatchObjectiveFunction
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluates the objective function for the provided match solution. Solution with the highest score will be selected
|
||||
/// as the solution. NOTE: This part of the match has not been implemented as of 2020-11-06 as it's only evaluating the
|
||||
/// first solution match (MatchOnce).
|
||||
/// </summary>
|
||||
decimal ComputeScore(OptionPositionCollection input, OptionStrategyMatch match, OptionPositionCollection unmatched);
|
||||
}
|
||||
}
|
||||
+34
@@ -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.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a default implementation of <see cref="IOptionStrategyDefinitionEnumerator"/> that enumerates
|
||||
/// definitions according to the order that they were provided to <see cref="OptionStrategyMatcherOptions"/>
|
||||
/// </summary>
|
||||
public class IdentityOptionStrategyDefinitionEnumerator : IOptionStrategyDefinitionEnumerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Enumerates the <paramref name="definitions"/> in the same order as provided.
|
||||
/// </summary>
|
||||
public IEnumerable<OptionStrategyDefinition> Enumerate(IReadOnlyList<OptionStrategyDefinition> definitions)
|
||||
{
|
||||
return definitions;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a lightweight structure representing a position in an option contract or underlying.
|
||||
/// This type is heavily utilized by the options strategy matcher and is the parameter type of
|
||||
/// option strategy definition predicates. Underlying quantities should be represented in lot sizes,
|
||||
/// which is equal to the quantity of shares divided by the contract's multiplier and then rounded
|
||||
/// down towards zero (truncate)
|
||||
/// </summary>
|
||||
public struct OptionPosition : IEquatable<OptionPosition>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a new <see cref="OptionPosition"/> with zero <see cref="Quantity"/>
|
||||
/// </summary>
|
||||
public static OptionPosition Empty(Symbol symbol)
|
||||
=> new OptionPosition(symbol, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not this position has any quantity
|
||||
/// </summary>
|
||||
public bool HasQuantity => Quantity != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not this position is for the underlying symbol
|
||||
/// </summary>
|
||||
public bool IsUnderlying => !Symbol.HasUnderlying;
|
||||
|
||||
/// <summary>
|
||||
/// Number of contracts held, can be positive or negative
|
||||
/// </summary>
|
||||
public int Quantity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Option contract symbol
|
||||
/// </summary>
|
||||
public Symbol Symbol { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying symbol. If this position represents the underlying,
|
||||
/// then this property is the same as the <see cref="Symbol"/> property
|
||||
/// </summary>
|
||||
public Symbol Underlying => IsUnderlying ? Symbol : Symbol.Underlying;
|
||||
|
||||
/// <summary>
|
||||
/// Option contract expiration date
|
||||
/// </summary>
|
||||
public DateTime Expiration
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Symbol.HasUnderlying)
|
||||
{
|
||||
return Symbol.ID.Date;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"{nameof(Expiration)} is not valid for underlying symbols: {Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Option contract strike price
|
||||
/// </summary>
|
||||
public decimal Strike
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Symbol.HasUnderlying)
|
||||
{
|
||||
return Symbol.ID.StrikePrice;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"{nameof(Strike)} is not valid for underlying symbols: {Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Option contract right (put/call)
|
||||
/// </summary>
|
||||
public OptionRight Right
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Symbol.HasUnderlying)
|
||||
{
|
||||
return Symbol.ID.OptionRight;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"{nameof(Right)} is not valid for underlying symbols: {Symbol}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this position is short/long/none
|
||||
/// </summary>
|
||||
public PositionSide Side => (PositionSide) Math.Sign(Quantity);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionPosition"/> structure
|
||||
/// </summary>
|
||||
/// <param name="symbol">The option contract symbol</param>
|
||||
/// <param name="quantity">The number of contracts held</param>
|
||||
public OptionPosition(Symbol symbol, int quantity)
|
||||
{
|
||||
Symbol = symbol;
|
||||
Quantity = quantity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionPosition"/> instance with negative <see cref="Quantity"/>
|
||||
/// </summary>
|
||||
public OptionPosition Negate()
|
||||
{
|
||||
return new OptionPosition(Symbol, -Quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionPosition"/> with this position's <see cref="Symbol"/>
|
||||
/// and the provided <paramref name="quantity"/>
|
||||
/// </summary>
|
||||
public OptionPosition WithQuantity(int quantity)
|
||||
{
|
||||
return new OptionPosition(Symbol, quantity);
|
||||
}
|
||||
|
||||
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
/// <returns>true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.</returns>
|
||||
public bool Equals(OptionPosition other)
|
||||
{
|
||||
return Equals(Symbol, other.Symbol) && Quantity == other.Quantity;
|
||||
}
|
||||
|
||||
/// <summary>Indicates whether this instance and a specified object are equal.</summary>
|
||||
/// <param name="obj">The object to compare with the current instance. </param>
|
||||
/// <returns>true if <paramref name="obj" /> and this instance are the same type and represent the same value; otherwise, false. </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((OptionPosition) obj);
|
||||
}
|
||||
|
||||
/// <summary>Returns the hash code for this instance.</summary>
|
||||
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return ((Symbol != null ? Symbol.GetHashCode() : 0) * 397) ^ Quantity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns the fully qualified type name of this instance.</summary>
|
||||
/// <returns>The fully qualified type name.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
var s = Quantity == 1 ? "" : "s";
|
||||
if (Symbol.HasUnderlying)
|
||||
{
|
||||
return $"{Quantity} {Right.ToLower()}{s} on {Symbol.Underlying.Value} at ${Strike} expiring on {Expiration:yyyy-MM-dd}";
|
||||
}
|
||||
|
||||
return $"{Quantity} share{s} of {Symbol.Value}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionPosition * Operator, will multiple quantity by given factor
|
||||
/// </summary>
|
||||
/// <param name="left">OptionPosition to operate on</param>
|
||||
/// <param name="factor">Factor to multiply by</param>
|
||||
/// <returns>Resulting OptionPosition</returns>
|
||||
public static OptionPosition operator *(OptionPosition left, int factor)
|
||||
{
|
||||
return new OptionPosition(left.Symbol, factor * left.Quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionPosition * Operator, will multiple quantity by given factor
|
||||
/// </summary>
|
||||
/// <param name="right">OptionPosition to operate on</param>
|
||||
/// <param name="factor">Factor to multiply by</param>
|
||||
/// <returns>Resulting OptionPosition</returns>
|
||||
public static OptionPosition operator *(int factor, OptionPosition right)
|
||||
{
|
||||
return new OptionPosition(right.Symbol, factor * right.Quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionPosition + Operator, will add quantities together if they are for the same symbol.
|
||||
/// </summary>
|
||||
/// <returns>Resulting OptionPosition</returns>
|
||||
public static OptionPosition operator +(OptionPosition left, OptionPosition right)
|
||||
{
|
||||
if (!Equals(left.Symbol, right.Symbol))
|
||||
{
|
||||
if (left == default(OptionPosition))
|
||||
{
|
||||
return right;
|
||||
}
|
||||
|
||||
if (right == default(OptionPosition))
|
||||
{
|
||||
return left;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unable to add OptionPosition instances with different symbols");
|
||||
}
|
||||
|
||||
return new OptionPosition(left.Symbol, left.Quantity + right.Quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionPosition - Operator, will subtract left - right quantities if they are for the same symbol.
|
||||
/// </summary>
|
||||
/// <returns>Resulting OptionPosition</returns>
|
||||
public static OptionPosition operator -(OptionPosition left, OptionPosition right)
|
||||
{
|
||||
if (!Equals(left.Symbol, right.Symbol))
|
||||
{
|
||||
if (left == default(OptionPosition))
|
||||
{
|
||||
// 0 - right
|
||||
return right.Negate();
|
||||
}
|
||||
|
||||
if (right == default(OptionPosition))
|
||||
{
|
||||
// left - 0
|
||||
return left;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Unable to subtract OptionPosition instances with different symbols");
|
||||
}
|
||||
|
||||
return new OptionPosition(left.Symbol, left.Quantity - right.Quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Option Position == Operator
|
||||
/// </summary>
|
||||
/// <returns>True if they are the same</returns>
|
||||
public static bool operator ==(OptionPosition left, OptionPosition right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Option Position != Operator
|
||||
/// </summary>
|
||||
/// <returns>True if they are not the same</returns>
|
||||
public static bool operator !=(OptionPosition left, OptionPosition right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* 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.Util;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using QuantConnect.Securities.Positions;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides indexing of option contracts
|
||||
/// </summary>
|
||||
public class OptionPositionCollection : IEnumerable<OptionPosition>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets an empty instance of <see cref="OptionPositionCollection"/>
|
||||
/// </summary>
|
||||
public static OptionPositionCollection Empty { get; } = new OptionPositionCollection(
|
||||
ImmutableDictionary<Symbol, OptionPosition>.Empty,
|
||||
ImmutableDictionary<OptionRight, ImmutableHashSet<Symbol>>.Empty,
|
||||
ImmutableDictionary<PositionSide, ImmutableHashSet<Symbol>>.Empty,
|
||||
ImmutableSortedDictionary<decimal, ImmutableHashSet<Symbol>>.Empty,
|
||||
ImmutableSortedDictionary<DateTime, ImmutableHashSet<Symbol>>.Empty
|
||||
);
|
||||
|
||||
private readonly ImmutableDictionary<Symbol, OptionPosition> _positions;
|
||||
private readonly ImmutableDictionary<OptionRight, ImmutableHashSet<Symbol>> _rights;
|
||||
private readonly ImmutableDictionary<PositionSide, ImmutableHashSet<Symbol>> _sides;
|
||||
private readonly ImmutableSortedDictionary<decimal, ImmutableHashSet<Symbol>> _strikes;
|
||||
private readonly ImmutableSortedDictionary<DateTime, ImmutableHashSet<Symbol>> _expirations;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying security's symbol
|
||||
/// </summary>
|
||||
public Symbol Underlying => UnderlyingPosition.Symbol ?? Symbol.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total count of unique positions, including the underlying
|
||||
/// </summary>
|
||||
public int Count => _positions.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether or not there's any positions in this collection.
|
||||
/// </summary>
|
||||
public bool IsEmpty => _positions.IsEmpty;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the quantity of underlying shares held
|
||||
/// TODO : Change to UnderlyingLots
|
||||
/// </summary>
|
||||
public int UnderlyingQuantity => UnderlyingPosition.Quantity;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of unique put contracts held (long or short)
|
||||
/// </summary>
|
||||
public int UniquePuts => _rights[OptionRight.Put].Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unique number of expirations
|
||||
/// </summary>
|
||||
public int UniqueExpirations => _expirations.Count;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of unique call contracts held (long or short)
|
||||
/// </summary>
|
||||
public int UniqueCalls => _rights[OptionRight.Call].Count;
|
||||
|
||||
/// <summary>
|
||||
/// Determines if this collection contains a position in the underlying
|
||||
/// </summary>
|
||||
public bool HasUnderlying => UnderlyingQuantity != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Underlying"/> position
|
||||
/// </summary>
|
||||
public OptionPosition UnderlyingPosition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets all unique strike prices in the collection, in ascending order.
|
||||
/// </summary>
|
||||
public IEnumerable<decimal> Strikes => _strikes.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all unique expiration dates in the collection, in chronological order.
|
||||
/// </summary>
|
||||
public IEnumerable<DateTime> Expirations => _expirations.Keys;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionPositionCollection"/> class
|
||||
/// </summary>
|
||||
/// <param name="positions">All positions</param>
|
||||
/// <param name="rights">Index of position symbols by option right</param>
|
||||
/// <param name="sides">Index of position symbols by position side (short/long/none)</param>
|
||||
/// <param name="strikes">Index of position symbols by strike price</param>
|
||||
/// <param name="expirations">Index of position symbols by expiration</param>
|
||||
public OptionPositionCollection(
|
||||
ImmutableDictionary<Symbol, OptionPosition> positions,
|
||||
ImmutableDictionary<OptionRight, ImmutableHashSet<Symbol>> rights,
|
||||
ImmutableDictionary<PositionSide, ImmutableHashSet<Symbol>> sides,
|
||||
ImmutableSortedDictionary<decimal, ImmutableHashSet<Symbol>> strikes,
|
||||
ImmutableSortedDictionary<DateTime, ImmutableHashSet<Symbol>> expirations
|
||||
)
|
||||
{
|
||||
_sides = sides;
|
||||
_rights = rights;
|
||||
_strikes = strikes;
|
||||
_positions = positions;
|
||||
_expirations = expirations;
|
||||
|
||||
if (_rights.Count != 2)
|
||||
{
|
||||
// ensure we always have both rights indexed, even if empty
|
||||
ImmutableHashSet<Symbol> value;
|
||||
if (!_rights.TryGetValue(OptionRight.Call, out value))
|
||||
{
|
||||
_rights = _rights.SetItem(OptionRight.Call, ImmutableHashSet<Symbol>.Empty);
|
||||
}
|
||||
if (!_rights.TryGetValue(OptionRight.Put, out value))
|
||||
{
|
||||
_rights = _rights.SetItem(OptionRight.Put, ImmutableHashSet<Symbol>.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
if (_sides.Count != 3)
|
||||
{
|
||||
// ensure we always have all three sides indexed, even if empty
|
||||
ImmutableHashSet<Symbol> value;
|
||||
if (!_sides.TryGetValue(PositionSide.None, out value))
|
||||
{
|
||||
_sides = _sides.SetItem(PositionSide.None, ImmutableHashSet<Symbol>.Empty);
|
||||
}
|
||||
if (!_sides.TryGetValue(PositionSide.Short, out value))
|
||||
{
|
||||
_sides = _sides.SetItem(PositionSide.Short, ImmutableHashSet<Symbol>.Empty);
|
||||
}
|
||||
if (!_sides.TryGetValue(PositionSide.Long, out value))
|
||||
{
|
||||
_sides = _sides.SetItem(PositionSide.Long, ImmutableHashSet<Symbol>.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
if (!positions.IsEmpty)
|
||||
{
|
||||
// assumption here is that 'positions' includes the underlying equity position and
|
||||
// ONLY option contracts, so all symbols have the underlying equity symbol embedded
|
||||
// via the Underlying property, except of course, for the underlying itself.
|
||||
var underlying = positions.First().Key;
|
||||
if (underlying.HasUnderlying)
|
||||
{
|
||||
underlying = underlying.Underlying;
|
||||
}
|
||||
|
||||
// OptionPosition is struct, so no worry about null ref via .Quantity
|
||||
var underlyingQuantity = positions.GetValueOrDefault(underlying).Quantity;
|
||||
UnderlyingPosition = new OptionPosition(underlying, underlyingQuantity);
|
||||
}
|
||||
#if DEBUG
|
||||
var errors = Validate().ToList();
|
||||
if (errors.Count > 0)
|
||||
{
|
||||
throw new ArgumentException("OptionPositionCollection validation failed: "
|
||||
+ Environment.NewLine + string.Join(Environment.NewLine, errors)
|
||||
);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if a position is held in the specified <paramref name="symbol"/>
|
||||
/// </summary>
|
||||
public bool HasPosition(Symbol symbol)
|
||||
{
|
||||
OptionPosition position;
|
||||
return TryGetPosition(symbol, out position) && position.Quantity != 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="OptionPosition"/> for the specified <paramref name="symbol"/>
|
||||
/// if one exists in this collection.
|
||||
/// </summary>
|
||||
public bool TryGetPosition(Symbol symbol, out OptionPosition position)
|
||||
{
|
||||
return _positions.TryGetValue(symbol, out position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionPositionCollection"/> from the specified enumerable of <paramref name="positions"/>
|
||||
/// </summary>
|
||||
public static OptionPositionCollection FromPositions(IEnumerable<OptionPosition> positions)
|
||||
{
|
||||
return Empty.AddRange(positions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionPositionCollection"/> from the specified enumerable of <paramref name="positions"/>
|
||||
/// </summary>
|
||||
public static OptionPositionCollection FromPositions(IEnumerable<IPosition> positions, decimal contractMultiplier)
|
||||
{
|
||||
return Empty.AddRange(positions.Select(position =>
|
||||
{
|
||||
var quantity = (int)position.Quantity;
|
||||
if (position.Symbol.SecurityType.HasOptions())
|
||||
{
|
||||
quantity = (int) (quantity / contractMultiplier);
|
||||
}
|
||||
return new OptionPosition(position.Symbol, quantity);
|
||||
}));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionPositionCollection"/> from the specified <paramref name="holdings"/>,
|
||||
/// filtering based on the <paramref name="underlying"/>
|
||||
/// </summary>
|
||||
public static OptionPositionCollection Create(Symbol underlying, decimal contractMultiplier, IEnumerable<SecurityHolding> holdings)
|
||||
{
|
||||
var positions = Empty;
|
||||
foreach (var holding in holdings)
|
||||
{
|
||||
var symbol = holding.Symbol;
|
||||
if (!symbol.HasUnderlying)
|
||||
{
|
||||
if (symbol == underlying)
|
||||
{
|
||||
var underlyingLots = (int) (holding.Quantity / contractMultiplier);
|
||||
positions = positions.Add(new OptionPosition(symbol, underlyingLots));
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (symbol.Underlying != underlying)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var position = new OptionPosition(symbol, (int) holding.Quantity);
|
||||
positions = positions.Add(position);
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new collection that is the result of adding the specified <paramref name="position"/> to this collection.
|
||||
/// </summary>
|
||||
public OptionPositionCollection Add(OptionPosition position)
|
||||
{
|
||||
if (!position.HasQuantity)
|
||||
{
|
||||
// adding nothing doesn't change the collection
|
||||
return this;
|
||||
}
|
||||
|
||||
var sides = _sides;
|
||||
var rights = _rights;
|
||||
var strikes = _strikes;
|
||||
var positions = _positions;
|
||||
var expirations = _expirations;
|
||||
|
||||
var exists = false;
|
||||
OptionPosition existing;
|
||||
var symbol = position.Symbol;
|
||||
if (positions.TryGetValue(symbol, out existing))
|
||||
{
|
||||
exists = true;
|
||||
position += existing;
|
||||
}
|
||||
|
||||
if (position.HasQuantity)
|
||||
{
|
||||
positions = positions.SetItem(symbol, position);
|
||||
if (!exists && symbol.HasUnderlying)
|
||||
{
|
||||
// update indexes when adding a new option contract
|
||||
sides = sides.Add(position.Side, symbol);
|
||||
rights = rights.Add(position.Right, symbol);
|
||||
strikes = strikes.Add(position.Strike, symbol);
|
||||
positions = positions.SetItem(symbol, position);
|
||||
expirations = expirations.Add(position.Expiration, symbol);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if the position's quantity went to zero, remove it entirely from the collection when
|
||||
// removing, be sure to remove strike/expiration indexes. we purposefully keep the rights
|
||||
// index populated, even with a zero count entry because it's bounded to 2 items (put/call)
|
||||
|
||||
positions = positions.Remove(symbol);
|
||||
if (symbol.HasUnderlying)
|
||||
{
|
||||
// keep call/put entries even if goes to zero
|
||||
var rightsValue = rights[position.Right].Remove(symbol);
|
||||
rights = rights.SetItem(position.Right, rightsValue);
|
||||
|
||||
// keep short/none/long entries even if goes to zero
|
||||
var sidesValue = sides[position.Side].Remove(symbol);
|
||||
sides = sides.SetItem(position.Side, sidesValue);
|
||||
|
||||
var strikesValue = strikes[position.Strike].Remove(symbol);
|
||||
strikes = strikesValue.Count > 0
|
||||
? strikes.SetItem(position.Strike, strikesValue)
|
||||
: strikes.Remove(position.Strike);
|
||||
|
||||
var expirationsValue = expirations[position.Expiration].Remove(symbol);
|
||||
expirations = expirationsValue.Count > 0
|
||||
? expirations.SetItem(position.Expiration, expirationsValue)
|
||||
: expirations.Remove(position.Expiration);
|
||||
}
|
||||
}
|
||||
|
||||
return new OptionPositionCollection(positions, rights, sides, strikes, expirations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new collection that is the result of removing the specified <paramref name="position"/>
|
||||
/// </summary>
|
||||
public OptionPositionCollection Remove(OptionPosition position)
|
||||
{
|
||||
return Add(position.Negate());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new collection that is the result of adding the specified <paramref name="positions"/> to this collection.
|
||||
/// </summary>
|
||||
public OptionPositionCollection AddRange(params OptionPosition[] positions)
|
||||
{
|
||||
return AddRange((IEnumerable<OptionPosition>) positions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new collection that is the result of adding the specified <paramref name="positions"/> to this collection.
|
||||
/// </summary>
|
||||
public OptionPositionCollection AddRange(IEnumerable<OptionPosition> positions)
|
||||
{
|
||||
return positions.Aggregate(this, (current, position) => current + position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new collection that is the result of removing the specified <paramref name="positions"/>
|
||||
/// </summary>
|
||||
public OptionPositionCollection RemoveRange(IEnumerable<OptionPosition> positions)
|
||||
{
|
||||
return AddRange(positions.Select(position => position.Negate()));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slices this collection, returning a new collection containing only
|
||||
/// positions with the specified <paramref name="right"/>
|
||||
/// </summary>
|
||||
public OptionPositionCollection Slice(OptionRight right, bool includeUnderlying = true)
|
||||
{
|
||||
var rights = _rights.Remove(right.Invert());
|
||||
|
||||
var positions = ImmutableDictionary<Symbol, OptionPosition>.Empty;
|
||||
if (includeUnderlying && HasUnderlying)
|
||||
{
|
||||
positions = positions.Add(Underlying, UnderlyingPosition);
|
||||
}
|
||||
|
||||
var sides = ImmutableDictionary<PositionSide, ImmutableHashSet<Symbol>>.Empty;
|
||||
var strikes = ImmutableSortedDictionary<decimal, ImmutableHashSet<Symbol>>.Empty;
|
||||
var expirations = ImmutableSortedDictionary<DateTime, ImmutableHashSet<Symbol>>.Empty;
|
||||
foreach (var symbol in rights.SelectMany(kvp => kvp.Value))
|
||||
{
|
||||
var position = _positions[symbol];
|
||||
sides = sides.Add(position.Side, symbol);
|
||||
positions = positions.Add(symbol, position);
|
||||
strikes = strikes.Add(position.Strike, symbol);
|
||||
expirations = expirations.Add(position.Expiration, symbol);
|
||||
}
|
||||
|
||||
return new OptionPositionCollection(positions, rights, sides, strikes, expirations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slices this collection, returning a new collection containing only
|
||||
/// positions with the specified <paramref name="side"/>
|
||||
/// </summary>
|
||||
public OptionPositionCollection Slice(PositionSide side, bool includeUnderlying = true)
|
||||
{
|
||||
var otherSides = GetOtherSides(side);
|
||||
var sides = _sides.Remove(otherSides[0]).Remove(otherSides[1]);
|
||||
|
||||
var positions = ImmutableDictionary<Symbol, OptionPosition>.Empty;
|
||||
if (includeUnderlying && HasUnderlying)
|
||||
{
|
||||
positions = positions.Add(Underlying, UnderlyingPosition);
|
||||
}
|
||||
|
||||
var rights = ImmutableDictionary<OptionRight, ImmutableHashSet<Symbol>>.Empty;
|
||||
var strikes = ImmutableSortedDictionary<decimal, ImmutableHashSet<Symbol>>.Empty;
|
||||
var expirations = ImmutableSortedDictionary<DateTime, ImmutableHashSet<Symbol>>.Empty;
|
||||
foreach (var symbol in sides.SelectMany(kvp => kvp.Value))
|
||||
{
|
||||
var position = _positions[symbol];
|
||||
rights = rights.Add(position.Right, symbol);
|
||||
positions = positions.Add(symbol, position);
|
||||
strikes = strikes.Add(position.Strike, symbol);
|
||||
expirations = expirations.Add(position.Expiration, symbol);
|
||||
}
|
||||
|
||||
return new OptionPositionCollection(positions, rights, sides, strikes, expirations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slices this collection, returning a new collection containing only
|
||||
/// positions matching the specified <paramref name="comparison"/> and <paramref name="strike"/>
|
||||
/// </summary>
|
||||
public OptionPositionCollection Slice(BinaryComparison comparison, decimal strike, bool includeUnderlying = true)
|
||||
{
|
||||
var strikes = comparison.Filter(_strikes, strike);
|
||||
if (strikes.IsEmpty)
|
||||
{
|
||||
return includeUnderlying && HasUnderlying ? Empty.Add(UnderlyingPosition) : Empty;
|
||||
}
|
||||
|
||||
var positions = ImmutableDictionary<Symbol, OptionPosition>.Empty;
|
||||
if (includeUnderlying)
|
||||
{
|
||||
OptionPosition underlyingPosition;
|
||||
if (_positions.TryGetValue(Underlying, out underlyingPosition))
|
||||
{
|
||||
positions = positions.Add(Underlying, underlyingPosition);
|
||||
}
|
||||
}
|
||||
|
||||
var sides = ImmutableDictionary<PositionSide, ImmutableHashSet<Symbol>>.Empty;
|
||||
var rights = ImmutableDictionary<OptionRight, ImmutableHashSet<Symbol>>.Empty;
|
||||
var expirations = ImmutableSortedDictionary<DateTime, ImmutableHashSet<Symbol>>.Empty;
|
||||
foreach (var symbol in strikes.SelectMany(kvp => kvp.Value))
|
||||
{
|
||||
var position = _positions[symbol];
|
||||
sides = sides.Add(position.Side, symbol);
|
||||
positions = positions.Add(symbol, position);
|
||||
rights = rights.Add(symbol.ID.OptionRight, symbol);
|
||||
expirations = expirations.Add(symbol.ID.Date, symbol);
|
||||
}
|
||||
|
||||
return new OptionPositionCollection(positions, rights, sides, strikes, expirations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Slices this collection, returning a new collection containing only
|
||||
/// positions matching the specified <paramref name="comparison"/> and <paramref name="expiration"/>
|
||||
/// </summary>
|
||||
public OptionPositionCollection Slice(BinaryComparison comparison, DateTime expiration, bool includeUnderlying = true)
|
||||
{
|
||||
var expirations = comparison.Filter(_expirations, expiration);
|
||||
if (expirations.IsEmpty)
|
||||
{
|
||||
return includeUnderlying && HasUnderlying ? Empty.Add(UnderlyingPosition) : Empty;
|
||||
}
|
||||
|
||||
var positions = ImmutableDictionary<Symbol, OptionPosition>.Empty;
|
||||
if (includeUnderlying)
|
||||
{
|
||||
OptionPosition underlyingPosition;
|
||||
if (_positions.TryGetValue(Underlying, out underlyingPosition))
|
||||
{
|
||||
positions = positions.Add(Underlying, underlyingPosition);
|
||||
}
|
||||
}
|
||||
|
||||
var sides = ImmutableDictionary<PositionSide, ImmutableHashSet<Symbol>>.Empty;
|
||||
var rights = ImmutableDictionary<OptionRight, ImmutableHashSet<Symbol>>.Empty;
|
||||
var strikes = ImmutableSortedDictionary<decimal, ImmutableHashSet<Symbol>>.Empty;
|
||||
foreach (var symbol in expirations.SelectMany(kvp => kvp.Value))
|
||||
{
|
||||
var position = _positions[symbol];
|
||||
sides = sides.Add(position.Side, symbol);
|
||||
positions = positions.Add(symbol, position);
|
||||
rights = rights.Add(symbol.ID.OptionRight, symbol);
|
||||
strikes = strikes.Add(symbol.ID.StrikePrice, symbol);
|
||||
}
|
||||
|
||||
return new OptionPositionCollection(positions, rights, sides, strikes, expirations);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the set of <see cref="OptionPosition"/> with the specified <paramref name="symbols"/>
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> ForSymbols(IEnumerable<Symbol> symbols)
|
||||
{
|
||||
foreach (var symbol in symbols)
|
||||
{
|
||||
OptionPosition position;
|
||||
if (_positions.TryGetValue(symbol, out position))
|
||||
{
|
||||
yield return position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the set of <see cref="OptionPosition"/> with the specified <paramref name="right"/>
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> ForRight(OptionRight right)
|
||||
{
|
||||
ImmutableHashSet<Symbol> symbols;
|
||||
return _rights.TryGetValue(right, out symbols)
|
||||
? ForSymbols(symbols)
|
||||
: Enumerable.Empty<OptionPosition>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the set of <see cref="OptionPosition"/> with the specified <paramref name="side"/>
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> ForSide(PositionSide side)
|
||||
{
|
||||
ImmutableHashSet<Symbol> symbols;
|
||||
return _sides.TryGetValue(side, out symbols)
|
||||
? ForSymbols(symbols)
|
||||
: Enumerable.Empty<OptionPosition>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the set of <see cref="OptionPosition"/> with the specified <paramref name="strike"/>
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> ForStrike(decimal strike)
|
||||
{
|
||||
ImmutableHashSet<Symbol> symbols;
|
||||
return _strikes.TryGetValue(strike, out symbols)
|
||||
? ForSymbols(symbols)
|
||||
: Enumerable.Empty<OptionPosition>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the set of <see cref="OptionPosition"/> with the specified <paramref name="expiration"/>
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> ForExpiration(DateTime expiration)
|
||||
{
|
||||
ImmutableHashSet<Symbol> symbols;
|
||||
return _expirations.TryGetValue(expiration, out symbols)
|
||||
? ForSymbols(symbols)
|
||||
: Enumerable.Empty<OptionPosition>();
|
||||
}
|
||||
|
||||
/// <summary>Returns a string that represents the current object.</summary>
|
||||
/// <returns>A string that represents the current object.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
if (Count == 0)
|
||||
{
|
||||
return "Empty";
|
||||
}
|
||||
|
||||
return HasUnderlying
|
||||
? $"{UnderlyingQuantity} {Underlying.Value}: {_positions.Count - 1} contract positions"
|
||||
: $"{Underlying.Value}: {_positions.Count} contract positions";
|
||||
}
|
||||
|
||||
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
||||
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
|
||||
public IEnumerator<OptionPosition> GetEnumerator()
|
||||
{
|
||||
return _positions.Select(kvp => kvp.Value).GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates this collection returning an enumerable of validation errors.
|
||||
/// This should only be invoked via tests and is automatically invoked via
|
||||
/// the constructor in DEBUG builds.
|
||||
/// </summary>
|
||||
internal IEnumerable<string> Validate()
|
||||
{
|
||||
foreach (var kvp in _positions)
|
||||
{
|
||||
var position = kvp.Value;
|
||||
var symbol = position.Symbol;
|
||||
if (position.Quantity == 0)
|
||||
{
|
||||
yield return $"{position}: Quantity == 0";
|
||||
}
|
||||
|
||||
if (!symbol.HasUnderlying)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ImmutableHashSet<Symbol> strikes;
|
||||
if (!_strikes.TryGetValue(position.Strike, out strikes) || !strikes.Contains(symbol))
|
||||
{
|
||||
yield return $"{position}: Not indexed by strike price";
|
||||
}
|
||||
|
||||
ImmutableHashSet<Symbol> expirations;
|
||||
if (!_expirations.TryGetValue(position.Expiration, out expirations) || !expirations.Contains(symbol))
|
||||
{
|
||||
yield return $"{position}: Not indexed by expiration date";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly PositionSide[] OtherSidesForNone = {PositionSide.Short, PositionSide.Long};
|
||||
private static readonly PositionSide[] OtherSidesForShort = {PositionSide.None, PositionSide.Long};
|
||||
private static readonly PositionSide[] OtherSidesForLong = {PositionSide.Short, PositionSide.None};
|
||||
private static PositionSide[] GetOtherSides(PositionSide side)
|
||||
{
|
||||
switch (side)
|
||||
{
|
||||
case PositionSide.Short: return OtherSidesForShort;
|
||||
case PositionSide.None: return OtherSidesForNone;
|
||||
case PositionSide.Long: return OtherSidesForLong;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(side), side, null);
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionPositionCollection + Operator
|
||||
/// </summary>
|
||||
/// <param name="positions">Collection to add to</param>
|
||||
/// <param name="position">OptionPosition to add</param>
|
||||
/// <returns>OptionPositionCollection with the new position added</returns>
|
||||
public static OptionPositionCollection operator+(OptionPositionCollection positions, OptionPosition position)
|
||||
{
|
||||
return positions.Add(position);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionPositionCollection - Operator
|
||||
/// </summary>
|
||||
/// <param name="positions">Collection to remove from</param>
|
||||
/// <param name="position">OptionPosition to remove</param>
|
||||
/// <returns>OptionPositionCollection with the position removed</returns>
|
||||
public static OptionPositionCollection operator-(OptionPositionCollection positions, OptionPosition position)
|
||||
{
|
||||
return positions.Remove(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a definitional object for an <see cref="OptionStrategy"/>. This definition is used to 'match' option
|
||||
/// positions via <see cref="OptionPositionCollection"/>. The <see cref="OptionStrategyMatcher"/> utilizes a full
|
||||
/// collection of these definitional objects in order to match an algorithm's option position holdings to the
|
||||
/// set of strategies in an effort to reduce the total margin required for holding the positions.
|
||||
/// </summary>
|
||||
public class OptionStrategyDefinition : IEnumerable<OptionStrategyLegDefinition>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the definition's name
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of underlying lots required to match this definition. A lot size
|
||||
/// is equal to the contract's multiplier and is usually equal to 100.
|
||||
/// </summary>
|
||||
public int UnderlyingLots { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the option leg definitions. This list does NOT contain a definition for the
|
||||
/// required underlying lots, due to its simplicity. Instead the required underlying
|
||||
/// lots are defined via the <see cref="UnderlyingLots"/> property of the definition.
|
||||
/// </summary>
|
||||
public IReadOnlyList<OptionStrategyLegDefinition> Legs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of legs, INCLUDING the underlying leg if applicable. This
|
||||
/// is used to perform a coarse filter as the minimum number of unique positions in
|
||||
/// the positions collection.
|
||||
/// </summary>
|
||||
public int LegCount => Legs.Count + (UnderlyingLots == 0 ? 0 : 1);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyDefinition"/> class
|
||||
/// </summary>
|
||||
/// <param name="name">The definition's name</param>
|
||||
/// <param name="underlyingLots">The required number of underlying lots</param>
|
||||
/// <param name="legs">Definitions for each option leg</param>
|
||||
public OptionStrategyDefinition(string name, int underlyingLots, IEnumerable<OptionStrategyLegDefinition> legs)
|
||||
{
|
||||
Name = name;
|
||||
Legs = legs.ToList();
|
||||
UnderlyingLots = underlyingLots;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="OptionStrategy"/> instance using this definition and the provided leg matches
|
||||
/// </summary>
|
||||
public OptionStrategy CreateStrategy(IReadOnlyList<OptionStrategyLegDefinitionMatch> legs)
|
||||
{
|
||||
return OptionStrategy.Create(Name, Enumerable.Range(0, Math.Min(Legs.Count, legs.Count)).Select(i => Legs[i].CreateLegData(legs[i])));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to match the positions to this definition exactly once, by evaluating the enumerable and
|
||||
/// taking the first entry matched. If not match is found, then false is returned and <paramref name="match"/>
|
||||
/// will be null.
|
||||
/// </summary>
|
||||
public bool TryMatchOnce(OptionStrategyMatcherOptions options, OptionPositionCollection positions, out OptionStrategyDefinitionMatch match)
|
||||
{
|
||||
match = Match(options, positions).FirstOrDefault();
|
||||
return match != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines all possible matches for this definition using the provided <paramref name="positions"/>.
|
||||
/// This includes OVERLAPPING matches. It's up to the actual matcher to make decisions based on which
|
||||
/// matches to accept. This allows the matcher to prioritize matching certain positions over others.
|
||||
/// </summary>
|
||||
public IEnumerable<OptionStrategyDefinitionMatch> Match(OptionPositionCollection positions)
|
||||
{
|
||||
return Match(OptionStrategyMatcherOptions.ForDefinitions(this), positions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines all possible matches for this definition using the provided <paramref name="positions"/>.
|
||||
/// This includes OVERLAPPING matches. It's up to the actual matcher to make decisions based on which
|
||||
/// matches to accept. This allows the matcher to prioritize matching certain positions over others.
|
||||
/// </summary>
|
||||
public IEnumerable<OptionStrategyDefinitionMatch> Match(
|
||||
OptionStrategyMatcherOptions options,
|
||||
OptionPositionCollection positions
|
||||
)
|
||||
{
|
||||
// TODO : Pass OptionStrategyMatcherOptions in and respect applicable options
|
||||
if (positions.Count < LegCount)
|
||||
{
|
||||
return Enumerable.Empty<OptionStrategyDefinitionMatch>();
|
||||
}
|
||||
|
||||
var multiplier = int.MaxValue;
|
||||
|
||||
// first check underlying lots has correct sign and sufficient magnitude
|
||||
var underlyingLotsSign = Math.Sign(UnderlyingLots);
|
||||
if (underlyingLotsSign != 0)
|
||||
{
|
||||
var underlyingPositionSign = Math.Sign(positions.UnderlyingQuantity);
|
||||
if (underlyingLotsSign != underlyingPositionSign ||
|
||||
Math.Abs(positions.UnderlyingQuantity) < Math.Abs(UnderlyingLots))
|
||||
{
|
||||
return Enumerable.Empty<OptionStrategyDefinitionMatch>();
|
||||
}
|
||||
|
||||
// set multiplier for underlying
|
||||
multiplier = positions.UnderlyingQuantity / UnderlyingLots;
|
||||
}
|
||||
|
||||
// TODO : Consider add OptionStrategyLegDefinition for underlying for consistency purposes.
|
||||
// Might want to enforce that it's always the first leg definition as well for easier slicing.
|
||||
return Match(options,
|
||||
ImmutableList<OptionStrategyLegDefinitionMatch>.Empty,
|
||||
ImmutableList<OptionPosition>.Empty,
|
||||
positions,
|
||||
multiplier
|
||||
).Distinct();
|
||||
}
|
||||
|
||||
private IEnumerable<OptionStrategyDefinitionMatch> Match(
|
||||
OptionStrategyMatcherOptions options,
|
||||
ImmutableList<OptionStrategyLegDefinitionMatch> legMatches,
|
||||
ImmutableList<OptionPosition> legPositions,
|
||||
OptionPositionCollection positions,
|
||||
int multiplier
|
||||
)
|
||||
{
|
||||
var nextLegIndex = legPositions.Count;
|
||||
if (nextLegIndex == Legs.Count)
|
||||
{
|
||||
if (nextLegIndex > 0)
|
||||
{
|
||||
yield return new OptionStrategyDefinitionMatch(this, legMatches, multiplier);
|
||||
}
|
||||
}
|
||||
else if (positions.Count >= LegCount - nextLegIndex)
|
||||
{
|
||||
// grab the next leg definition and perform the match, restricting total to configured maximum per leg
|
||||
var nextLeg = Legs[nextLegIndex];
|
||||
var maxLegMatch = options.GetMaximumLegMatches(nextLegIndex);
|
||||
foreach (var legMatch in nextLeg.Match(options, legPositions, positions).Take(maxLegMatch))
|
||||
{
|
||||
// add match to the match we're constructing and deduct matched position from positions collection
|
||||
// we track the min multiplier in line so when we're done, we have the total number of matches for
|
||||
// the matched set of positions in this 'thread' (OptionStrategy.Quantity)
|
||||
foreach (var definitionMatch in Match(options,
|
||||
legMatches.Add(legMatch),
|
||||
legPositions.Add(legMatch.Position),
|
||||
positions - legMatch.Position,
|
||||
Math.Min(multiplier, legMatch.Multiplier)
|
||||
))
|
||||
{
|
||||
yield return definitionMatch;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// positions.Count < LegsCount indicates a failed match
|
||||
|
||||
// could include partial matches, would allow an algorithm to determine if adding a
|
||||
// new position could help reduce overall margin exposure by completing a strategy
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns a string that represents the current object.</summary>
|
||||
/// <returns>A string that represents the current object.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory function for creating definitions
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition Create(string name, int underlyingLots, params OptionStrategyLegDefinition[] legs)
|
||||
{
|
||||
return new OptionStrategyDefinition(name, underlyingLots, legs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory function for creating definitions
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition Create(string name, params OptionStrategyLegDefinition[] legs)
|
||||
{
|
||||
return new OptionStrategyDefinition(name, 0, legs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory function for creating definitions
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition Create(string name, params Func<Builder, Builder>[] predicates)
|
||||
{
|
||||
return predicates.Aggregate(new Builder(name),
|
||||
(builder, predicate) => predicate(builder)
|
||||
).Build();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory function for creating a call leg definition
|
||||
/// </summary>
|
||||
public static OptionStrategyLegDefinition CallLeg(int quantity,
|
||||
params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
|
||||
)
|
||||
{
|
||||
return OptionStrategyLegDefinition.Create(OptionRight.Call, quantity, predicates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Factory function for creating a put leg definition
|
||||
/// </summary>
|
||||
public static OptionStrategyLegDefinition PutLeg(int quantity,
|
||||
params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
|
||||
)
|
||||
{
|
||||
return OptionStrategyLegDefinition.Create(OptionRight.Put, quantity, predicates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builder class supporting fluent syntax in constructing <see cref="OptionStrategyDefinition"/>.
|
||||
/// </summary>
|
||||
public class Builder
|
||||
{
|
||||
private readonly string _name;
|
||||
|
||||
private int _underlyingLots;
|
||||
private List<OptionStrategyLegDefinition> _legs;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Builder"/> class
|
||||
/// </summary>
|
||||
public Builder(string name)
|
||||
{
|
||||
_name = name;
|
||||
_legs = new List<OptionStrategyLegDefinition>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the required number of underlying lots
|
||||
/// </summary>
|
||||
public Builder WithUnderlyingLots(int lots)
|
||||
{
|
||||
if (_underlyingLots != 0)
|
||||
{
|
||||
throw new InvalidOperationException("Underlying lots has already been set.");
|
||||
}
|
||||
|
||||
_underlyingLots = lots;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a call leg
|
||||
/// </summary>
|
||||
public Builder WithCall(int quantity,
|
||||
params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
|
||||
)
|
||||
{
|
||||
_legs.Add(OptionStrategyLegDefinition.Create(OptionRight.Call, quantity, predicates));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a put leg
|
||||
/// </summary>
|
||||
public Builder WithPut(int quantity,
|
||||
params Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>[] predicates
|
||||
)
|
||||
{
|
||||
_legs.Add(OptionStrategyLegDefinition.Create(OptionRight.Put, quantity, predicates));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the <see cref="OptionStrategyDefinition"/>
|
||||
/// </summary>
|
||||
public OptionStrategyDefinition Build()
|
||||
{
|
||||
return new OptionStrategyDefinition(_name, _underlyingLots, _legs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
||||
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
|
||||
public IEnumerator<OptionStrategyLegDefinition> GetEnumerator()
|
||||
{
|
||||
return Legs.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <summary>Returns an enumerator that iterates through a collection.</summary>
|
||||
/// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a match of <see cref="OptionPosition"/> to a <see cref="OptionStrategyDefinition"/>
|
||||
/// </summary>
|
||||
public class OptionStrategyDefinitionMatch : IEquatable<OptionStrategyDefinitionMatch>
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="OptionStrategyDefinition"/> matched
|
||||
/// </summary>
|
||||
public OptionStrategyDefinition Definition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of times the definition is able to match the available positions.
|
||||
/// Since definitions are formed at the 'unit' level, such as having 1 contract,
|
||||
/// the multiplier defines how many times the definition matched. This multiplier
|
||||
/// is used to scale the quantity defined in each leg definition when creating the
|
||||
/// <see cref="OptionStrategy"/> objects.
|
||||
/// </summary>
|
||||
public int Multiplier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="OptionStrategyLegDefinitionMatch"/> instances matched to the definition.
|
||||
/// </summary>
|
||||
public IReadOnlyList<OptionStrategyLegDefinitionMatch> Legs { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyDefinitionMatch"/> class
|
||||
/// </summary>
|
||||
public OptionStrategyDefinitionMatch(
|
||||
OptionStrategyDefinition definition,
|
||||
IReadOnlyList<OptionStrategyLegDefinitionMatch> legs,
|
||||
int multiplier
|
||||
)
|
||||
{
|
||||
Legs = legs;
|
||||
Multiplier = multiplier;
|
||||
Definition = definition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deducts the matched positions from the specified <paramref name="positions"/> taking into account the multiplier
|
||||
/// </summary>
|
||||
public OptionPositionCollection RemoveFrom(OptionPositionCollection positions)
|
||||
{
|
||||
var optionPositions = Legs.Select(leg => leg.CreateOptionPosition(Multiplier));
|
||||
if (Definition.UnderlyingLots != 0)
|
||||
{
|
||||
optionPositions = optionPositions.Concat(new[]
|
||||
{
|
||||
new OptionPosition(Legs[0].Position.Symbol.Underlying, Definition.UnderlyingLots * Multiplier)
|
||||
});
|
||||
}
|
||||
return positions.RemoveRange(optionPositions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the <see cref="OptionStrategy"/> instance this match represents
|
||||
/// </summary>
|
||||
public OptionStrategy CreateStrategy()
|
||||
{
|
||||
var legs = Legs
|
||||
// if Definition.UnderlyingLots is not 0, we will create the underlying leg separately
|
||||
.Where(leg => leg.Position.Symbol.HasUnderlying || Definition.UnderlyingLots == 0)
|
||||
.Select(leg => leg.CreateOptionStrategyLeg(Multiplier));
|
||||
|
||||
if (Definition.UnderlyingLots != 0)
|
||||
{
|
||||
legs = legs.Concat([OptionStrategy.UnderlyingLegData.Create(Definition.UnderlyingLots * Multiplier, Legs[0].Position.Underlying)]);
|
||||
}
|
||||
|
||||
return OptionStrategy.Create(Definition.Name, legs);
|
||||
}
|
||||
|
||||
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
/// <returns>true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.</returns>
|
||||
public bool Equals(OptionStrategyDefinitionMatch other)
|
||||
{
|
||||
if (ReferenceEquals(null, other))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Equals(Definition, other.Definition))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// index legs by OptionPosition so we can do the equality while ignoring ordering
|
||||
var positions = other.Legs.ToDictionary(leg => leg.Position, leg => leg.Multiplier);
|
||||
foreach (var leg in other.Legs)
|
||||
{
|
||||
int multiplier;
|
||||
if (!positions.TryGetValue(leg.Position, out multiplier))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (leg.Multiplier != multiplier)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>Determines whether the specified object is equal to the current object.</summary>
|
||||
/// <param name="obj">The object to compare with the current object. </param>
|
||||
/// <returns>true if the specified object is equal to the current object; otherwise, false.</returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ReferenceEquals(this, obj))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (obj.GetType() != GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return Equals((OptionStrategyDefinitionMatch) obj);
|
||||
}
|
||||
|
||||
/// <summary>Serves as the default hash function. </summary>
|
||||
/// <returns>A hash code for the current object.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
// we want to ensure that the ordering of legs does not impact equality operators in
|
||||
// pursuit of this, we compute the hash codes of each leg, placing them into an array
|
||||
// and then sort the array. using the sorted array, aggregates the hash codes
|
||||
|
||||
var hashCode = Definition.GetHashCode();
|
||||
var arr = new int[Legs.Count];
|
||||
for (int i = 0; i < Legs.Count; i++)
|
||||
{
|
||||
arr[i] = Legs[i].GetHashCode();
|
||||
}
|
||||
|
||||
Array.Sort(arr);
|
||||
|
||||
for (int i = 0; i < arr.Length; i++)
|
||||
{
|
||||
hashCode = (hashCode * 397) ^ arr[i];
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns a string that represents the current object.</summary>
|
||||
/// <returns>A string that represents the current object.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Definition.Name}: {string.Join("|", Legs.Select(leg => leg.Position))}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionStrategyDefinitionMatch == Operator
|
||||
/// </summary>
|
||||
/// <returns>True if they are the same</returns>
|
||||
public static bool operator ==(OptionStrategyDefinitionMatch left, OptionStrategyDefinitionMatch right)
|
||||
{
|
||||
return Equals(left, right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionStrategyDefinitionMatch != Operator
|
||||
/// </summary>
|
||||
/// <returns>True if they are not the same</returns>
|
||||
public static bool operator !=(OptionStrategyDefinitionMatch left, OptionStrategyDefinitionMatch right)
|
||||
{
|
||||
return !Equals(left, right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* 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.Immutable;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides a listing of pre-defined <see cref="OptionStrategyDefinition"/>
|
||||
/// These definitions are blueprints for <see cref="OptionStrategy"/> instances.
|
||||
/// Factory functions for those can be found at <see cref="OptionStrategies"/>
|
||||
/// </summary>
|
||||
public static class OptionStrategyDefinitions
|
||||
{
|
||||
// lazy since 'AllDefinitions' is at top of file and static members are evaluated in order
|
||||
private static readonly Lazy<ImmutableList<OptionStrategyDefinition>> All
|
||||
= new Lazy<ImmutableList<OptionStrategyDefinition>>(() =>
|
||||
typeof(OptionStrategyDefinitions)
|
||||
.GetProperties(BindingFlags.Public | BindingFlags.Static)
|
||||
.Where(property => property.PropertyType == typeof(OptionStrategyDefinition))
|
||||
.Select(property => (OptionStrategyDefinition)property.GetValue(null))
|
||||
.ToImmutableList()
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Collection of all OptionStrategyDefinitions
|
||||
/// </summary>
|
||||
public static ImmutableList<OptionStrategyDefinition> AllDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
var strategies = All.Value;
|
||||
|
||||
return strategies
|
||||
.SelectMany(optionStrategy => {
|
||||
// when selling the strategy can get reverted and it's still valid, we need the definition to match against
|
||||
var inverted = new OptionStrategyDefinition(optionStrategy.Name, optionStrategy.UnderlyingLots * -1,
|
||||
optionStrategy.Legs.Select(leg => new OptionStrategyLegDefinition(leg.Right, leg.Quantity * -1, leg)));
|
||||
|
||||
if (strategies.Any(strategy => strategy.UnderlyingLots == inverted.UnderlyingLots
|
||||
&& strategy.Legs.Count == inverted.Legs.Count
|
||||
&& strategy.Legs.All(leg => inverted.Legs.
|
||||
Any(invertedLeg => invertedLeg.Right == leg.Right
|
||||
&& leg.Quantity == invertedLeg.Quantity
|
||||
&& leg.All(predicate => invertedLeg.Any(invertedPredicate => invertedPredicate.ToString() == predicate.ToString()))))))
|
||||
{
|
||||
// some strategies inverted have a different name we already know, let's skip those
|
||||
return new[] { optionStrategy };
|
||||
}
|
||||
return new[] { optionStrategy, inverted };
|
||||
})
|
||||
.ToImmutableList();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hold 1 lot of the underlying and sell 1 call contract
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="ProtectiveCall"/></remarks>
|
||||
public static OptionStrategyDefinition CoveredCall { get; }
|
||||
= OptionStrategyDefinition.Create("Covered Call", 1,
|
||||
OptionStrategyDefinition.CallLeg(-1)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Hold -1 lot of the underlying and buy 1 call contract
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="CoveredCall"/></remarks>
|
||||
public static OptionStrategyDefinition ProtectiveCall { get; }
|
||||
= OptionStrategyDefinition.Create("Protective Call", -1,
|
||||
OptionStrategyDefinition.CallLeg(1)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Hold -1 lot of the underlying and sell 1 put contract
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="ProtectivePut"/></remarks>
|
||||
public static OptionStrategyDefinition CoveredPut { get; }
|
||||
= OptionStrategyDefinition.Create("Covered Put", -1,
|
||||
OptionStrategyDefinition.PutLeg(-1)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Hold 1 lot of the underlying and buy 1 put contract
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="CoveredPut"/></remarks>
|
||||
public static OptionStrategyDefinition ProtectivePut { get; }
|
||||
= OptionStrategyDefinition.Create("Protective Put", 1,
|
||||
OptionStrategyDefinition.PutLeg(1)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Hold 1 lot of the underlying, sell 1 call contract and buy 1 put contract.
|
||||
/// The strike price of the short call is below the strike of the long put with the same expiration.
|
||||
/// </summary>
|
||||
/// <remarks>Combination of <see cref="CoveredCall"/> and <see cref="ProtectivePut"/></remarks>
|
||||
public static OptionStrategyDefinition ProtectiveCollar { get; }
|
||||
= OptionStrategyDefinition.Create("Protective Collar", 1,
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Hold 1 lot of the underlying, sell 1 call contract and buy 1 put contract.
|
||||
/// The strike price of the call and put are the same, with the same expiration.
|
||||
/// </summary>
|
||||
/// <remarks>A special case of <see cref="ProtectiveCollar"/></remarks>
|
||||
public static OptionStrategyDefinition Conversion { get; }
|
||||
= OptionStrategyDefinition.Create("Conversion", 1,
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Hold 1 lot of the underlying, sell 1 call contract and buy 1 put contract.
|
||||
/// The strike price of the call and put are the same, with the same expiration.
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of <see cref="Conversion"/></remarks>
|
||||
public static OptionStrategyDefinition ReverseConversion { get; }
|
||||
= OptionStrategyDefinition.Create("Reverse Conversion", -1,
|
||||
OptionStrategyDefinition.CallLeg(1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Sell 1 call contract without holding the underlying
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition NakedCall { get; }
|
||||
= OptionStrategyDefinition.Create("Naked Call",
|
||||
OptionStrategyDefinition.CallLeg(-1)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Sell 1 put contract without holding the underlying
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition NakedPut { get; }
|
||||
= OptionStrategyDefinition.Create("Naked Put",
|
||||
OptionStrategyDefinition.PutLeg(-1)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bear Call Spread strategy consists of two calls with the same expiration but different strikes.
|
||||
/// The strike price of the short call is below the strike of the long call. This is a credit spread.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BearCallSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Bear Call Spread",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, p) => p.Strike > legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bear Put Spread strategy consists of two puts with the same expiration but different strikes.
|
||||
/// The strike price of the short put is below the strike of the long put. This is a debit spread.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BearPutSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Bear Put Spread",
|
||||
OptionStrategyDefinition.PutLeg(1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bull Call Spread strategy consists of two calls with the same expiration but different strikes.
|
||||
/// The strike price of the short call is higher than the strike of the long call. This is a debit spread.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BullCallSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Bull Call Spread",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, p) => p.Strike > legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Method creates new Bull Put Spread strategy, that consists of two puts with the same expiration but
|
||||
/// different strikes. The strike price of the short put is above the strike of the long put. This is a
|
||||
/// credit spread.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BullPutSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Bull Put Spread",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Straddle strategy is a combination of buying a call and buying a put, both with the same strike price
|
||||
/// and expiration.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition Straddle { get; }
|
||||
= OptionStrategyDefinition.Create("Straddle",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Straddle strategy is a combination of selling a call and selling a put, both with the same strike price
|
||||
/// and expiration.
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="Straddle"/></remarks>
|
||||
public static OptionStrategyDefinition ShortStraddle { get; }
|
||||
= OptionStrategyDefinition.Create("Short Straddle",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Strangle strategy consists of buying a call option and a put option with the same expiration date.
|
||||
/// The strike price of the call is above the strike of the put.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition Strangle { get; }
|
||||
= OptionStrategyDefinition.Create("Strangle",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Strangle strategy consists of selling a call option and a put option with the same expiration date.
|
||||
/// The strike price of the call is above the strike of the put.
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="Strangle"/></remarks>
|
||||
public static OptionStrategyDefinition ShortStrangle { get; }
|
||||
= OptionStrategyDefinition.Create("Short Strangle",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Butterfly Call strategy consists of two short calls at a middle strike, and one long call each at a lower
|
||||
/// and upper strike. The upper and lower strikes must both be equidistant from the middle strike.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ButterflyCall { get; }
|
||||
= OptionStrategyDefinition.Create("Butterfly Call",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.CallLeg(-2, (legs, p) => p.Strike >= legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, p) => p.Strike >= legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration,
|
||||
(legs, p) => p.Strike - legs[1].Strike == legs[1].Strike - legs[0].Strike)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Butterfly Call strategy consists of two long calls at a middle strike, and one short call each at a lower
|
||||
/// and upper strike. The upper and lower strikes must both be equidistant from the middle strike.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortButterflyCall { get; }
|
||||
= OptionStrategyDefinition.Create("Short Butterfly Call",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.CallLeg(+2, (legs, p) => p.Strike >= legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, p) => p.Strike >= legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration,
|
||||
(legs, p) => p.Strike - legs[1].Strike == legs[1].Strike - legs[0].Strike)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Butterfly Put strategy consists of two short puts at a middle strike, and one long put each at a lower and
|
||||
/// upper strike. The upper and lower strikes must both be equidistant from the middle strike.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ButterflyPut { get; }
|
||||
= OptionStrategyDefinition.Create("Butterfly Put",
|
||||
OptionStrategyDefinition.PutLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(-2, (legs, p) => p.Strike >= legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike >= legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration,
|
||||
(legs, p) => p.Strike - legs[1].Strike == legs[1].Strike - legs[0].Strike)
|
||||
);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Short Butterfly Put strategy consists of two long puts at a middle strike, and one short put each at a lower and
|
||||
/// upper strike. The upper and lower strikes must both be equidistant from the middle strike.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortButterflyPut { get; }
|
||||
= OptionStrategyDefinition.Create("Short Butterfly Put",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+2, (legs, p) => p.Strike >= legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike >= legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration,
|
||||
(legs, p) => p.Strike - legs[1].Strike == legs[1].Strike - legs[0].Strike)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Call Calendar Spread strategy is a short one call option and long a second call option with a more distant
|
||||
/// expiration.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition CallCalendarSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Call Calendar Spread",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration > legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Call Calendar Spread strategy is long one call option and short a second call option with a more distant
|
||||
/// expiration.
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="CallCalendarSpread"/></remarks>
|
||||
public static OptionStrategyDefinition ShortCallCalendarSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Short Call Calendar Spread",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration > legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Put Calendar Spread strategy is a short one put option and long a second put option with a more distant
|
||||
/// expiration.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition PutCalendarSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Put Calendar Spread",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration > legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Put Calendar Spread strategy is long one put option and short a second put option with a more distant
|
||||
/// expiration.
|
||||
/// </summary>
|
||||
/// <remarks>Inverse of the <see cref="PutCalendarSpread"/></remarks>
|
||||
public static OptionStrategyDefinition ShortPutCalendarSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Short Put Calendar Spread",
|
||||
OptionStrategyDefinition.PutLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration > legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Iron Butterfly strategy consists of a short ATM call, a short ATM put, a long OTM call, and a long OTM put.
|
||||
/// The strike spread between ATM and OTM call and put are the same. All at the same expiration date.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition IronButterfly { get; }
|
||||
= OptionStrategyDefinition.Create("Iron Butterfly",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, c) => c.Strike == legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, c) => c.Strike == legs[0].Strike * 2 - legs[1].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Iron Butterfly strategy consists of a long ATM call, a long ATM put, a short OTM call, and a short OTM put.
|
||||
/// The strike spread between ATM and OTM call and put are the same. All at the same expiration date.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortIronButterfly { get; }
|
||||
= OptionStrategyDefinition.Create("Short Iron Butterfly",
|
||||
OptionStrategyDefinition.PutLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, c) => c.Strike == legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, c) => c.Strike == legs[0].Strike * 2 - legs[1].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Iron Condor strategy is buying a put, selling a put with a higher strike price, selling a call and buying a call with a higher strike price.
|
||||
/// All at the same expiration date
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition IronCondor { get; }
|
||||
= OptionStrategyDefinition.Create("Iron Condor",
|
||||
OptionStrategyDefinition.PutLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike > legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, p) => p.Strike > legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(1, (legs, p) => p.Strike > legs[2].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Iron Condor strategy is selling a put, buying a put with a higher strike price, buying a call and selling a call with a higher strike price.
|
||||
/// All at the same expiration date
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortIronCondor { get; }
|
||||
= OptionStrategyDefinition.Create("Short Iron Condor",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike > legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, p) => p.Strike > legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, p) => p.Strike > legs[2].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Long Box Spread strategy is long 1 call and short 1 put with the same strike,
|
||||
/// while short 1 call and long 1 put with a higher, same strike. All options have the same expiry.
|
||||
/// expiration.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BoxSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Box Spread",
|
||||
OptionStrategyDefinition.PutLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, c) => c.Strike == legs[1].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, c) => c.Strike == legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Box Spread strategy is short 1 call and long 1 put with the same strike,
|
||||
/// while long 1 call and short 1 put with a higher, same strike. All options have the same expiry.
|
||||
/// expiration.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortBoxSpread { get; }
|
||||
= OptionStrategyDefinition.Create("Short Box Spread",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, c) => c.Strike == legs[1].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, c) => c.Strike == legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Jelly Roll is short 1 call and long 1 call with the same strike but further expiry, together with
|
||||
/// long 1 put and short 1 put with the same strike and expiries as calls.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition JellyRoll { get; }
|
||||
= OptionStrategyDefinition.Create("Jelly Roll",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, c) => c.Strike == legs[0].Strike,
|
||||
(legs, c) => c.Expiration > legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[1].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Jelly Roll is long 1 call and short 1 call with the same strike but further expiry, together with
|
||||
/// short 1 put and long 1 put with the same strike and expiries as calls.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortJellyRoll { get; }
|
||||
= OptionStrategyDefinition.Create("Short Jelly Roll",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, c) => c.Strike == legs[0].Strike,
|
||||
(legs, c) => c.Expiration > legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike == legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[1].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bear Call Ladder strategy is short 1 call and long 2 calls, with ascending strike prices in order,
|
||||
/// All options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BearCallLadder { get; }
|
||||
= OptionStrategyDefinition.Create("Bear Call Ladder",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, c) => c.Strike > legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(+1, (legs, c) => c.Strike > legs[1].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bear Put Ladder strategy is long 1 put and short 2 puts, with descending strike prices in order,
|
||||
/// All options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BearPutLadder { get; }
|
||||
= OptionStrategyDefinition.Create("Bear Put Ladder",
|
||||
OptionStrategyDefinition.PutLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(-1, (legs, p) => p.Strike < legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bull Call Ladder strategy is long 1 call and short 2 calls, with ascending strike prices in order,
|
||||
/// All options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BullCallLadder { get; }
|
||||
= OptionStrategyDefinition.Create("Bull Call Ladder",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, c) => c.Strike > legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.CallLeg(-1, (legs, c) => c.Strike > legs[1].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Bull Put Ladder strategy is short 1 put and long 2 puts, with descending strike prices in order,
|
||||
/// All options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition BullPutLadder { get; }
|
||||
= OptionStrategyDefinition.Create("Bull Put Ladder",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration),
|
||||
OptionStrategyDefinition.PutLeg(+1, (legs, p) => p.Strike < legs[1].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Call Backspread strategy is short 1 call and long 2 calls, with ascending strike prices in order,
|
||||
/// both options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition CallBackspread { get; }
|
||||
= OptionStrategyDefinition.Create("Call Backspread",
|
||||
OptionStrategyDefinition.CallLeg(-1),
|
||||
OptionStrategyDefinition.CallLeg(+2, (legs, c) => c.Strike > legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Put Backspread strategy is short 1 put and long 2 puts, with descending strike prices in order,
|
||||
/// both options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition PutBackspread { get; }
|
||||
= OptionStrategyDefinition.Create("Put Backspread",
|
||||
OptionStrategyDefinition.PutLeg(-1),
|
||||
OptionStrategyDefinition.PutLeg(+2, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Call Backspread strategy is long 1 call and short 2 calls, with ascending strike prices in order,
|
||||
/// both options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortCallBackspread { get; }
|
||||
= OptionStrategyDefinition.Create("Short Call Backspread",
|
||||
OptionStrategyDefinition.CallLeg(+1),
|
||||
OptionStrategyDefinition.CallLeg(-2, (legs, c) => c.Strike > legs[0].Strike,
|
||||
(legs, c) => c.Expiration == legs[0].Expiration)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Short Put Backspread strategy is long 1 put and short 2 puts, with descending strike prices in order,
|
||||
/// both options have the same expiry.
|
||||
/// </summary>
|
||||
public static OptionStrategyDefinition ShortPutBackspread { get; }
|
||||
= OptionStrategyDefinition.Create("Short Put Backspread",
|
||||
OptionStrategyDefinition.PutLeg(+1),
|
||||
OptionStrategyDefinition.PutLeg(-2, (legs, p) => p.Strike < legs[0].Strike,
|
||||
(legs, p) => p.Expiration == legs[0].Expiration)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using QuantConnect.Orders;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a single option leg in an option strategy. This definition supports direct
|
||||
/// match (does position X match the definition) and position collection filtering (filter
|
||||
/// collection to include matches)
|
||||
/// </summary>
|
||||
public class OptionStrategyLegDefinition : IEnumerable<OptionStrategyLegPredicate>
|
||||
{
|
||||
private readonly OptionStrategyLegPredicate[] _predicates;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the unit quantity
|
||||
/// </summary>
|
||||
public int Quantity { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the contract right
|
||||
/// </summary>
|
||||
public OptionRight Right { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyLegDefinition"/> class
|
||||
/// </summary>
|
||||
/// <param name="right">The leg's contract right</param>
|
||||
/// <param name="quantity">The leg's unit quantity</param>
|
||||
/// <param name="predicates">The conditions a position must meet in order to match this definition</param>
|
||||
public OptionStrategyLegDefinition(OptionRight right, int quantity, IEnumerable<OptionStrategyLegPredicate> predicates)
|
||||
{
|
||||
Right = right;
|
||||
Quantity = quantity;
|
||||
_predicates = predicates.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Yields all possible matches for this leg definition held within the collection of <paramref name="positions"/>
|
||||
/// </summary>
|
||||
/// <param name="options">Strategy matcher options guiding matching behaviors</param>
|
||||
/// <param name="legs">The preceding legs already matched for the parent strategy definition</param>
|
||||
/// <param name="positions">The remaining, unmatched positions available to be matched against</param>
|
||||
/// <returns>An enumerable of potential matches</returns>
|
||||
public IEnumerable<OptionStrategyLegDefinitionMatch> Match(
|
||||
OptionStrategyMatcherOptions options,
|
||||
IReadOnlyList<OptionPosition> legs,
|
||||
OptionPositionCollection positions
|
||||
)
|
||||
{
|
||||
foreach (var position in options.Enumerate(Filter(legs, positions, false)))
|
||||
{
|
||||
var multiplier = position.Quantity / Quantity;
|
||||
if (multiplier != 0)
|
||||
{
|
||||
yield return new OptionStrategyLegDefinitionMatch(multiplier,
|
||||
position.WithQuantity(multiplier * Quantity)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters the provided <paramref name="positions"/> collection such that any remaining positions are all
|
||||
/// valid options that match this leg definition instance.
|
||||
/// </summary>
|
||||
public OptionPositionCollection Filter(IReadOnlyList<OptionPosition> legs, OptionPositionCollection positions, bool includeUnderlying = true)
|
||||
{
|
||||
// first filter down to applicable right
|
||||
positions = positions.Slice(Right, includeUnderlying);
|
||||
if (positions.IsEmpty)
|
||||
{
|
||||
return positions;
|
||||
}
|
||||
|
||||
// second filter according to the required side
|
||||
var side = (PositionSide) Math.Sign(Quantity);
|
||||
positions = positions.Slice(side, includeUnderlying);
|
||||
if (positions.IsEmpty)
|
||||
{
|
||||
return positions;
|
||||
}
|
||||
|
||||
// these are ordered such that indexed filters are performed force and
|
||||
// opaque/complex predicates follow since they require full table scans
|
||||
foreach (var predicate in _predicates)
|
||||
{
|
||||
positions = predicate.Filter(legs, positions, includeUnderlying);
|
||||
if (positions.IsEmpty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point, every position in the positions
|
||||
// collection is a valid match for this definition
|
||||
return positions;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the appropriate <see cref="Leg"/> for the specified <paramref name="match"/>
|
||||
/// </summary>
|
||||
public Leg CreateLegData(OptionStrategyLegDefinitionMatch match)
|
||||
{
|
||||
return CreateLegData(
|
||||
match.Position.Symbol,
|
||||
match.Position.Quantity / Quantity
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the appropriate <see cref="OptionStrategy.LegData"/> with the specified <paramref name="quantity"/>
|
||||
/// </summary>
|
||||
public static Leg CreateLegData(Symbol symbol, int quantity)
|
||||
{
|
||||
if (symbol.SecurityType == SecurityType.Option)
|
||||
{
|
||||
return OptionStrategy.OptionLegData.Create(quantity, symbol);
|
||||
}
|
||||
|
||||
return OptionStrategy.UnderlyingLegData.Create(quantity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not this leg definition matches the specified <paramref name="position"/>,
|
||||
/// and if so, what the resulting quantity of the <see cref="OptionStrategy.OptionLegData"/> should be.
|
||||
/// </summary>
|
||||
public bool TryMatch(OptionPosition position, out Leg leg)
|
||||
{
|
||||
if (Right != position.Right ||
|
||||
Math.Sign(Quantity) != Math.Sign(position.Quantity))
|
||||
{
|
||||
leg = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
var quantity = position.Quantity / Quantity;
|
||||
if (quantity == 0)
|
||||
{
|
||||
leg = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
leg = position.Symbol.SecurityType == SecurityType.Option
|
||||
? (Leg) OptionStrategy.OptionLegData.Create(quantity, position.Symbol)
|
||||
: OptionStrategy.UnderlyingLegData.Create(quantity);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionStrategyLegDefinition"/> matching the specified parameters
|
||||
/// </summary>
|
||||
public static OptionStrategyLegDefinition Create(OptionRight right, int quantity,
|
||||
IEnumerable<Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>>> predicates
|
||||
)
|
||||
{
|
||||
return new OptionStrategyLegDefinition(right, quantity,
|
||||
// sort predicates such that indexed predicates are evaluated first
|
||||
// this leaves fewer positions to be evaluated by the full table scan
|
||||
predicates.Select(OptionStrategyLegPredicate.Create).OrderBy(p => p.IsIndexed ? 0 : 1)
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Returns an enumerator that iterates through the collection.</summary>
|
||||
/// <returns>An enumerator that can be used to iterate through the collection.</returns>
|
||||
public IEnumerator<OptionStrategyLegPredicate> GetEnumerator()
|
||||
{
|
||||
foreach (var predicate in _predicates)
|
||||
{
|
||||
yield return predicate;
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 QuantConnect.Orders;
|
||||
using System;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the item result type of <see cref="OptionStrategyLegDefinition.Match"/>, containing the number of
|
||||
/// times the leg definition matched the position (<see cref="Multiplier"/>) and applicable portion of the position.
|
||||
/// </summary>
|
||||
public struct OptionStrategyLegDefinitionMatch : IEquatable<OptionStrategyLegDefinitionMatch>
|
||||
{
|
||||
/// <summary>
|
||||
/// The number of times the definition is able to match the position. For example,
|
||||
/// if the definition requires +2 contracts and the algorithm's position has +5
|
||||
/// contracts, then this multiplier would equal 2.
|
||||
/// </summary>
|
||||
public int Multiplier { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The position that was successfully matched with the total quantity matched. For example,
|
||||
/// if the definition requires +2 contracts and this multiplier equals 2, then this position
|
||||
/// would have a quantity of 4. This may be different than the remaining/total quantity
|
||||
/// available in the positions collection.
|
||||
/// </summary>
|
||||
public OptionPosition Position { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyLegDefinitionMatch"/> struct
|
||||
/// </summary>
|
||||
/// <param name="multiplier">The number of times the positions matched the leg definition</param>
|
||||
/// <param name="position">The position that matched the leg definition</param>
|
||||
public OptionStrategyLegDefinitionMatch(int multiplier, OptionPosition position)
|
||||
{
|
||||
Position = position;
|
||||
Multiplier = multiplier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the appropriate type of <see cref="Leg"/> for this matched position
|
||||
/// </summary>
|
||||
/// <param name="multiplier">The multiplier to use for creating the leg data. This multiplier will be
|
||||
/// the minimum multiplier of all legs within a strategy definition match. Each leg defines its own
|
||||
/// multiplier which is the max matches for that leg and the strategy definition's multiplier is the
|
||||
/// min of the individual legs.</param>
|
||||
public Leg CreateOptionStrategyLeg(int multiplier)
|
||||
{
|
||||
var quantity = Position.Quantity;
|
||||
if (Multiplier != multiplier)
|
||||
{
|
||||
if (multiplier > Multiplier)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(multiplier), "Unable to create strategy leg with a larger multiplier than matched.");
|
||||
}
|
||||
|
||||
// back out the unit quantity and scale it up to the requested multiplier
|
||||
var unit = Position.Quantity / Multiplier;
|
||||
quantity = unit * multiplier;
|
||||
}
|
||||
|
||||
return Position.IsUnderlying
|
||||
? (Leg) OptionStrategy.UnderlyingLegData.Create(quantity, Position.Symbol)
|
||||
: OptionStrategy.OptionLegData.Create(quantity, Position.Symbol);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the appropriate <see cref="OptionPosition"/> for this matched position
|
||||
/// </summary>
|
||||
/// <param name="multiplier">The multiplier to use for creating the OptionPosition. This multiplier will be
|
||||
/// the minimum multiplier of all legs within a strategy definition match. Each leg defines its own
|
||||
/// multiplier which is the max matches for that leg and the strategy definition's multiplier is the
|
||||
/// min of the individual legs.</param>
|
||||
public OptionPosition CreateOptionPosition(int multiplier)
|
||||
{
|
||||
var quantity = Position.Quantity;
|
||||
if (Multiplier != multiplier)
|
||||
{
|
||||
if (multiplier > Multiplier)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(multiplier), "Unable to create strategy leg with a larger multiplier than matched.");
|
||||
}
|
||||
|
||||
// back out the unit quantity and scale it up to the requested multiplier
|
||||
var unit = Position.Quantity / Multiplier;
|
||||
quantity = unit * multiplier;
|
||||
}
|
||||
|
||||
return new OptionPosition(Position.Symbol, quantity);
|
||||
}
|
||||
|
||||
/// <summary>Indicates whether the current object is equal to another object of the same type.</summary>
|
||||
/// <param name="other">An object to compare with this object.</param>
|
||||
/// <returns>true if the current object is equal to the <paramref name="other" /> parameter; otherwise, false.</returns>
|
||||
public bool Equals(OptionStrategyLegDefinitionMatch other)
|
||||
{
|
||||
return Multiplier == other.Multiplier && Position.Equals(other.Position);
|
||||
}
|
||||
|
||||
/// <summary>Indicates whether this instance and a specified object are equal.</summary>
|
||||
/// <param name="obj">The object to compare with the current instance. </param>
|
||||
/// <returns>true if <paramref name="obj" /> and this instance are the same type and represent the same value; otherwise, false. </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (ReferenceEquals(null, obj))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return obj is OptionStrategyLegDefinitionMatch && Equals((OptionStrategyLegDefinitionMatch) obj);
|
||||
}
|
||||
|
||||
/// <summary>Returns the hash code for this instance.</summary>
|
||||
/// <returns>A 32-bit signed integer that is the hash code for this instance.</returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Multiplier * 397) ^ Position.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns the fully qualified type name of this instance.</summary>
|
||||
/// <returns>The fully qualified type name.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{Multiplier} Matches|{Position}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionStrategyLegDefinitionMatch == Operator
|
||||
/// </summary>
|
||||
/// <returns>True if they are equal</returns>
|
||||
public static bool operator ==(OptionStrategyLegDefinitionMatch left, OptionStrategyLegDefinitionMatch right)
|
||||
{
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// OptionStrategyLegDefinitionMatch != Operator
|
||||
/// </summary>
|
||||
/// <returns>True if they are not equal</returns>
|
||||
public static bool operator !=(OptionStrategyLegDefinitionMatch left, OptionStrategyLegDefinitionMatch right)
|
||||
{
|
||||
return !left.Equals(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using QuantConnect.Util;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a condition under which a particular <see cref="OptionPosition"/> can be combined with
|
||||
/// a preceding list of leg (also of type <see cref="OptionPosition"/>) to achieve a particular
|
||||
/// option strategy.
|
||||
/// </summary>
|
||||
public class OptionStrategyLegPredicate
|
||||
{
|
||||
private readonly BinaryComparison _comparison;
|
||||
private readonly IOptionStrategyLegPredicateReferenceValue _reference;
|
||||
private readonly Func<IReadOnlyList<OptionPosition>, OptionPosition, bool> _predicate;
|
||||
private readonly Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>> _expression;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not this predicate is able to utilize <see cref="OptionPositionCollection"/> indexes.
|
||||
/// </summary>
|
||||
public bool IsIndexed => _comparison != null && _reference != null;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyLegPredicate"/> class
|
||||
/// </summary>
|
||||
/// <param name="comparison">The <see cref="BinaryComparison"/> invoked</param>
|
||||
/// <param name="reference">The reference value, such as a strike price, encapsulated within the
|
||||
/// <see cref="IOptionStrategyLegPredicateReferenceValue"/> to enable resolving the value from different potential sets.</param>
|
||||
/// <param name="predicate">The compiled predicate expression</param>
|
||||
/// <param name="expression">The predicate expression, from which, all other values were derived.</param>
|
||||
public OptionStrategyLegPredicate(
|
||||
BinaryComparison comparison,
|
||||
IOptionStrategyLegPredicateReferenceValue reference,
|
||||
Func<IReadOnlyList<OptionPosition>, OptionPosition, bool> predicate,
|
||||
Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>> expression
|
||||
)
|
||||
{
|
||||
_reference = reference;
|
||||
_predicate = predicate;
|
||||
_comparison = comparison;
|
||||
_expression = expression;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether or not the provided combination of preceding <paramref name="legs"/>
|
||||
/// and current <paramref name="position"/> adhere to this predicate's requirements.
|
||||
/// </summary>
|
||||
public bool Matches(IReadOnlyList<OptionPosition> legs, OptionPosition position)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _predicate(legs, position);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
// attempt to access option SecurityIdentifier values, such as strike, on the underlying
|
||||
// this simply means we don't match and can safely ignore this exception. now, this does
|
||||
// somewhat indicate a potential design flaw, but I content that this is better than having
|
||||
// to manage the underlying position separately throughout the entire matching process.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters the specified <paramref name="positions"/> by applying this predicate based on the referenced legs.
|
||||
/// </summary>
|
||||
public OptionPositionCollection Filter(IReadOnlyList<OptionPosition> legs, OptionPositionCollection positions, bool includeUnderlying)
|
||||
{
|
||||
if (!IsIndexed)
|
||||
{
|
||||
// if the predicate references non-indexed properties or contains complex/multiple conditions then
|
||||
// we'll need to do a full table scan. this is not always avoidable, but we should try to avoid it
|
||||
return OptionPositionCollection.Empty.AddRange(
|
||||
positions.Where(position => _predicate(legs, position))
|
||||
);
|
||||
}
|
||||
|
||||
var referenceValue = _reference.Resolve(legs);
|
||||
switch (_reference.Target)
|
||||
{
|
||||
case PredicateTargetValue.Right: return positions.Slice((OptionRight) referenceValue, includeUnderlying);
|
||||
case PredicateTargetValue.Strike: return positions.Slice(_comparison, (decimal) referenceValue, includeUnderlying);
|
||||
case PredicateTargetValue.Expiration: return positions.Slice(_comparison, (DateTime) referenceValue, includeUnderlying);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the underlying <see cref="IOptionStrategyLegPredicateReferenceValue"/> value used by this predicate.
|
||||
/// </summary>
|
||||
public IOptionStrategyLegPredicateReferenceValue GetReferenceValue()
|
||||
{
|
||||
return _reference;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionStrategyLegPredicate"/> from the specified predicate <paramref name="expression"/>
|
||||
/// </summary>
|
||||
public static OptionStrategyLegPredicate Create(
|
||||
Expression<Func<IReadOnlyList<OptionPosition>, OptionPosition, bool>> expression
|
||||
)
|
||||
{
|
||||
// expr must NOT include compound comparisons
|
||||
// expr is a lambda of one of the following forms:
|
||||
// (legs, position) => position.{target} {comparison} legs[i].{reference-target}
|
||||
// (legs, position) => legs[i].{reference-target} {comparison} position.{target}
|
||||
// (legs, position) => position.{target} {comparison} {literal-reference-target}
|
||||
// (legs, position) => {literal-reference-target} {comparison} position.{target}
|
||||
|
||||
// we want to make the comparison of a common form, specifically:
|
||||
// position.{target} {comparison} {reference-target}
|
||||
// this is so when we invoke OptionPositionCollection we have the correct comparison type
|
||||
// for example, legs[0].Strike > position.Strike
|
||||
// needs to be inverted into position.Strike < legs[0].Strike
|
||||
// so we can call OptionPositionCollection.Slice(BinaryComparison.LessThan, legs[0].Strike);
|
||||
|
||||
try
|
||||
{
|
||||
var legsParameter = expression.Parameters[0];
|
||||
var positionParameter = expression.Parameters[1];
|
||||
var binary = expression.OfType<BinaryExpression>().Single(e => e.NodeType.IsBinaryComparison());
|
||||
var comparison = BinaryComparison.FromExpressionType(binary.NodeType);
|
||||
var leftReference = CreateReferenceValue(legsParameter, positionParameter, binary.Left);
|
||||
var rightReference = CreateReferenceValue(legsParameter, positionParameter, binary.Right);
|
||||
if (leftReference != null && rightReference != null)
|
||||
{
|
||||
throw new ArgumentException($"The provided expression is not of the required form: {expression}");
|
||||
}
|
||||
|
||||
// we want the left side to be null, indicating position.{target}
|
||||
// if not, then we need to flip the comparison operand
|
||||
var reference = rightReference;
|
||||
if (rightReference == null)
|
||||
{
|
||||
reference = leftReference;
|
||||
comparison = comparison.FlipOperands();
|
||||
}
|
||||
|
||||
return new OptionStrategyLegPredicate(comparison, reference, expression.Compile(), expression);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// we can still handle arbitrary predicates, they just require a full search of the positions
|
||||
// as we're unable to leverage any of the pre-build indexes via Slice methods.
|
||||
return new OptionStrategyLegPredicate(null, null, expression.Compile(), expression);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="IOptionStrategyLegPredicateReferenceValue"/> from the specified lambda parameters
|
||||
/// and expression to be evaluated.
|
||||
/// </summary>
|
||||
private static IOptionStrategyLegPredicateReferenceValue CreateReferenceValue(
|
||||
Expression legsParameter,
|
||||
Expression positionParameter,
|
||||
Expression expression
|
||||
)
|
||||
{
|
||||
// if we're referencing the position parameter then this isn't a reference value
|
||||
// this 'value' is the positions being matched in OptionPositionCollection
|
||||
// verify the legs parameter doesn't appear in here either
|
||||
var expressions = expression.AsEnumerable().ToList();
|
||||
var containsLegParameter = expressions.Any(e => ReferenceEquals(e, legsParameter));
|
||||
var containsPositionParameter = expressions.Any(e => ReferenceEquals(e, positionParameter));
|
||||
if (containsPositionParameter)
|
||||
{
|
||||
if (containsLegParameter)
|
||||
{
|
||||
throw new NotSupportedException("Expressions containing references to both parameters " +
|
||||
"(legs and positions) on the same side of an equality operator are not supported."
|
||||
);
|
||||
}
|
||||
|
||||
// this expression is of the form position.Strike/position.Expiration/position.Right
|
||||
// and as such, is not a reference value, simply return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!containsLegParameter)
|
||||
{
|
||||
// this is a literal and we'll attempt to evaluate it.
|
||||
var value = Expression.Lambda(expression).Compile().DynamicInvoke();
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException($"Failed to evaluate expression literal: {expressions}");
|
||||
}
|
||||
|
||||
return ConstantOptionStrategyLegReferenceValue.Create(value);
|
||||
}
|
||||
|
||||
// we're looking for an array indexer into the legs list
|
||||
var methodCall = expressions.Single<MethodCallExpression>();
|
||||
Debug.Assert(methodCall.Method.Name == "get_Item");
|
||||
// compile and dynamically invoke the argument to get_Item(x) {legs[x]}
|
||||
var arrayIndex = (int) Expression.Lambda(methodCall.Arguments[0]).Compile().DynamicInvoke();
|
||||
|
||||
// and then a member expression denoting the property (target)
|
||||
var member = expressions.Single<MemberExpression>().Member;
|
||||
var target = GetPredicateTargetValue(member.Name);
|
||||
|
||||
return new OptionStrategyLegPredicateReferenceValue(arrayIndex, target);
|
||||
}
|
||||
|
||||
private static PredicateTargetValue GetPredicateTargetValue(string memberName)
|
||||
{
|
||||
switch (memberName)
|
||||
{
|
||||
case nameof(OptionPosition.Right): return PredicateTargetValue.Right;
|
||||
case nameof(OptionPosition.Strike): return PredicateTargetValue.Strike;
|
||||
case nameof(OptionPosition.Expiration): return PredicateTargetValue.Expiration;
|
||||
default:
|
||||
throw new NotImplementedException(
|
||||
$"Failed to resolve member name '{memberName}' to {nameof(PredicateTargetValue)}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Returns a string that represents the current object.</summary>
|
||||
/// <returns>A string that represents the current object.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return _expression.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an implementation of <see cref="IOptionStrategyLegPredicateReferenceValue"/> that references an option
|
||||
/// leg from the list of already matched legs by index. The property referenced is defined by <see cref="PredicateTargetValue"/>
|
||||
/// </summary>
|
||||
public class OptionStrategyLegPredicateReferenceValue : IOptionStrategyLegPredicateReferenceValue
|
||||
{
|
||||
private readonly int _index;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the target of this value
|
||||
/// </summary>
|
||||
public PredicateTargetValue Target { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="IOptionStrategyLegPredicateReferenceValue"/> class
|
||||
/// </summary>
|
||||
/// <param name="index">The legs list index</param>
|
||||
/// <param name="target">The property value being referenced</param>
|
||||
public OptionStrategyLegPredicateReferenceValue(int index, PredicateTargetValue target)
|
||||
{
|
||||
_index = index;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the value of the comparand specified in an <see cref="OptionStrategyLegPredicate"/>.
|
||||
/// For example, the predicate may include ... > legs[0].Strike, and upon evaluation, we need to
|
||||
/// be able to extract leg[0].Strike for the currently contemplated set of legs adhering to a
|
||||
/// strategy's definition.
|
||||
/// </summary>
|
||||
public object Resolve(IReadOnlyList<OptionPosition> legs)
|
||||
{
|
||||
if (_index >= legs.Count)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
$"OptionStrategyLegPredicateReferenceValue[{_index}] is unable to be resolved. Only {legs.Count} legs were provided."
|
||||
);
|
||||
}
|
||||
|
||||
var leg = legs[_index];
|
||||
switch (Target)
|
||||
{
|
||||
case PredicateTargetValue.Right: return leg.Right;
|
||||
case PredicateTargetValue.Strike: return leg.Strike;
|
||||
case PredicateTargetValue.Expiration: return leg.Expiration;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a complete result from running the matcher on a collection of positions.
|
||||
/// The matching process will return one these matches for every potential combination
|
||||
/// of strategies conforming to the search settings and the positions provided.
|
||||
/// </summary>
|
||||
public class OptionStrategyMatch
|
||||
{
|
||||
/// <summary>
|
||||
/// The strategies that were matched
|
||||
/// </summary>
|
||||
public List<OptionStrategy> Strategies { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyMatch"/> class
|
||||
/// </summary>
|
||||
public OptionStrategyMatch(List<OptionStrategy> strategies)
|
||||
{
|
||||
Strategies = strategies;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Matches <see cref="OptionPositionCollection"/> against a collection of <see cref="OptionStrategyDefinition"/>
|
||||
/// according to the <see cref="OptionStrategyMatcherOptions"/> provided.
|
||||
/// </summary>
|
||||
public class OptionStrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies options controlling how the matcher operates
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions Options { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyMatcher"/> class
|
||||
/// </summary>
|
||||
/// <param name="options">Specifies definitions and other options controlling the matcher</param>
|
||||
public OptionStrategyMatcher(OptionStrategyMatcherOptions options)
|
||||
{
|
||||
Options = options;
|
||||
}
|
||||
|
||||
// TODO : Implement matching multiple permutations and using the objective function to select the best solution
|
||||
|
||||
/// <summary>
|
||||
/// Using the definitions provided in <see cref="Options"/>, attempts to match all <paramref name="positions"/>.
|
||||
/// The resulting <see cref="OptionStrategyMatch"/> presents a single, valid solution for matching as many positions
|
||||
/// as possible.
|
||||
/// </summary>
|
||||
public OptionStrategyMatch MatchOnce(OptionPositionCollection positions)
|
||||
{
|
||||
// these definitions are enumerated according to the configured IOptionStrategyDefinitionEnumerator
|
||||
|
||||
var strategies = new List<OptionStrategy>();
|
||||
foreach (var definition in Options.Definitions)
|
||||
{
|
||||
// simplest implementation here is to match one at a time, updating positions in between
|
||||
// a better implementation would be to evaluate all possible matches and make decisions
|
||||
// prioritizing positions that would require more margin if not matched
|
||||
|
||||
OptionStrategyDefinitionMatch match;
|
||||
while (definition.TryMatchOnce(Options, positions, out match))
|
||||
{
|
||||
positions = match.RemoveFrom(positions);
|
||||
strategies.Add(match.CreateStrategy());
|
||||
}
|
||||
|
||||
if (positions.IsEmpty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new OptionStrategyMatch(strategies);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines options that influence how the matcher operates.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Many properties in this type are not implemented in the matcher but are provided to document
|
||||
/// the types of things that can be added to the matcher in the future as necessary. Some of the
|
||||
/// features contemplated in this class would require updating the various matching/filtering/slicing
|
||||
/// functions to accept these options, or a particular property. This is the case for the enumerators
|
||||
/// which would be used to prioritize which positions to try and match first. A great implementation
|
||||
/// of the <see cref="IOptionPositionCollectionEnumerator"/> would be to yield positions with the
|
||||
/// highest margin requirements first. At time of writing, the goal is to achieve a workable rev0,
|
||||
/// and we can later improve the efficiency/optimization of the matching process.
|
||||
/// </remarks>
|
||||
public class OptionStrategyMatcherOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The maximum amount of time spent trying to find an optimal solution.
|
||||
/// </summary>
|
||||
public TimeSpan MaximumDuration { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The maximum number of matches to evaluate for the entire portfolio.
|
||||
/// </summary>
|
||||
public int MaximumSolutionCount { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indexed by leg index, defines the max matches to evaluate per leg.
|
||||
/// For example, MaximumCountPerLeg[1] is the max matches to evaluate
|
||||
/// for the second leg (index=1).
|
||||
/// </summary>
|
||||
public IReadOnlyList<int> MaximumCountPerLeg { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The definitions to be used for matching.
|
||||
/// </summary>
|
||||
public IEnumerable<OptionStrategyDefinition> Definitions
|
||||
=> _definitionEnumerator.Enumerate(_definitions);
|
||||
|
||||
/// <summary>
|
||||
/// Objective function used to compare different match solutions for a given set of positions/definitions
|
||||
/// </summary>
|
||||
public IOptionStrategyMatchObjectiveFunction ObjectiveFunction { get; }
|
||||
|
||||
private readonly IReadOnlyList<OptionStrategyDefinition> _definitions;
|
||||
private readonly IOptionPositionCollectionEnumerator _positionEnumerator;
|
||||
private readonly IOptionStrategyDefinitionEnumerator _definitionEnumerator;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OptionStrategyMatcherOptions"/> class, providing
|
||||
/// options that control the behavior of the <see cref="OptionStrategyMatcher"/>
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions(
|
||||
IReadOnlyList<OptionStrategyDefinition> definitions,
|
||||
IReadOnlyList<int> maximumCountPerLeg,
|
||||
TimeSpan maximumDuration = default(TimeSpan),
|
||||
int maximumSolutionCount = 100,
|
||||
IOptionStrategyDefinitionEnumerator definitionEnumerator = null,
|
||||
IOptionStrategyMatchObjectiveFunction objectiveFunction = null,
|
||||
IOptionPositionCollectionEnumerator positionEnumerator = null
|
||||
)
|
||||
{
|
||||
if (maximumDuration == default(TimeSpan))
|
||||
{
|
||||
maximumDuration = Time.OneMinute;
|
||||
}
|
||||
|
||||
if (definitionEnumerator == null)
|
||||
{
|
||||
// by default we want more complex option strategies to have matching priority
|
||||
definitionEnumerator = new DescendingByLegCountOptionStrategyDefinitionEnumerator();
|
||||
}
|
||||
|
||||
if (objectiveFunction == null)
|
||||
{
|
||||
objectiveFunction = new UnmatchedPositionCountOptionStrategyMatchObjectiveFunction();
|
||||
}
|
||||
|
||||
if (positionEnumerator == null)
|
||||
{
|
||||
positionEnumerator = new DefaultOptionPositionCollectionEnumerator();
|
||||
}
|
||||
|
||||
_definitions = definitions;
|
||||
MaximumDuration = maximumDuration;
|
||||
ObjectiveFunction = objectiveFunction;
|
||||
MaximumCountPerLeg = maximumCountPerLeg;
|
||||
_positionEnumerator = positionEnumerator;
|
||||
_definitionEnumerator = definitionEnumerator;
|
||||
MaximumSolutionCount = maximumSolutionCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum number of leg matches to be evaluated. This is to limit evaluating exponential
|
||||
/// numbers of potential matches as a result of large numbers of unique option positions for the same
|
||||
/// underlying security.
|
||||
/// </summary>
|
||||
public int GetMaximumLegMatches(int legIndex)
|
||||
{
|
||||
return MaximumCountPerLeg[legIndex];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates the specified <paramref name="positions"/> according to the configured
|
||||
/// <see cref="IOptionPositionCollectionEnumerator"/>
|
||||
/// </summary>
|
||||
public IEnumerable<OptionPosition> Enumerate(OptionPositionCollection positions)
|
||||
{
|
||||
return _positionEnumerator.Enumerate(positions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionStrategyMatcherOptions"/> with the specified <paramref name="definitions"/>,
|
||||
/// with no limits of maximum matches per leg and default values for the remaining options
|
||||
/// </summary>
|
||||
public static OptionStrategyMatcherOptions ForDefinitions(params OptionStrategyDefinition[] definitions)
|
||||
{
|
||||
return ForDefinitions(definitions.AsEnumerable());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="OptionStrategyMatcherOptions"/> with the specified <paramref name="definitions"/>,
|
||||
/// with no limits of maximum matches per leg and default values for the remaining options
|
||||
/// </summary>
|
||||
public static OptionStrategyMatcherOptions ForDefinitions(IEnumerable<OptionStrategyDefinition> definitions)
|
||||
{
|
||||
var maximumCountPerLeg = new[] {int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue, int.MaxValue};
|
||||
return new OptionStrategyMatcherOptions(definitions.ToList(), maximumCountPerLeg);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the maximum time provided for obtaining an optimal solution.
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions WithMaximumDuration(TimeSpan duration)
|
||||
{
|
||||
return new OptionStrategyMatcherOptions(
|
||||
_definitions,
|
||||
MaximumCountPerLeg,
|
||||
duration,
|
||||
MaximumSolutionCount,
|
||||
_definitionEnumerator,
|
||||
ObjectiveFunction,
|
||||
_positionEnumerator
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the maximum number of solutions to evaluate via the objective function.
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions WithMaximumSolutionCount(int count)
|
||||
{
|
||||
return new OptionStrategyMatcherOptions(
|
||||
_definitions,
|
||||
MaximumCountPerLeg,
|
||||
MaximumDuration,
|
||||
count,
|
||||
_definitionEnumerator,
|
||||
ObjectiveFunction,
|
||||
_positionEnumerator
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the maximum number of solutions per leg index in a solution. Matching is a recursive
|
||||
/// process, for example, we'll find a very large number of positions to match the first leg. Matching
|
||||
/// the second leg we'll see less, and third still even less. This is because each subsequent leg must
|
||||
/// abide by all the previous legs. This parameter defines how many potential matches to evaluate at
|
||||
/// each leg. For the first leg, we'll evaluate counts[0] matches. For the second leg we'll evaluate
|
||||
/// counts[1] matches and so on. By decreasing this parameter we can evaluate more total, complete
|
||||
/// solutions for the entire portfolio rather than evaluation every single permutation of matches for
|
||||
/// a particular strategy definition, which grows in absurd exponential fashion as the portfolio grows.
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions WithMaximumCountPerLeg(IReadOnlyList<int> counts)
|
||||
{
|
||||
return new OptionStrategyMatcherOptions(
|
||||
_definitions,
|
||||
counts,
|
||||
MaximumDuration,
|
||||
MaximumSolutionCount,
|
||||
_definitionEnumerator,
|
||||
ObjectiveFunction,
|
||||
_positionEnumerator
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies a function used to evaluate how desirable a particular solution is. A good implementation for
|
||||
/// this would be to minimize the total margin required to hold all of the positions.
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions WithObjectiveFunction(IOptionStrategyMatchObjectiveFunction function)
|
||||
{
|
||||
return new OptionStrategyMatcherOptions(
|
||||
_definitions,
|
||||
MaximumCountPerLeg,
|
||||
MaximumDuration,
|
||||
MaximumSolutionCount,
|
||||
_definitionEnumerator,
|
||||
function,
|
||||
_positionEnumerator
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the order in which definitions are evaluated. Definitions evaluated sooner are more likely to
|
||||
/// find matches than ones evaluated later.
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions WithDefinitionEnumerator(IOptionStrategyDefinitionEnumerator enumerator)
|
||||
{
|
||||
return new OptionStrategyMatcherOptions(
|
||||
_definitions,
|
||||
MaximumCountPerLeg,
|
||||
MaximumDuration,
|
||||
MaximumSolutionCount,
|
||||
enumerator,
|
||||
ObjectiveFunction,
|
||||
_positionEnumerator
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the order in which positions are evaluated. Positions evaluated sooner are more likely to
|
||||
/// find matches than ones evaluated later. A good implementation for this is its stand-alone margin required,
|
||||
/// which would encourage the algorithm to match higher margin positions before matching lower margin positiosn.
|
||||
/// </summary>
|
||||
public OptionStrategyMatcherOptions WithPositionEnumerator(IOptionPositionCollectionEnumerator enumerator)
|
||||
{
|
||||
return new OptionStrategyMatcherOptions(
|
||||
_definitions,
|
||||
MaximumCountPerLeg,
|
||||
MaximumDuration,
|
||||
MaximumSolutionCount,
|
||||
_definitionEnumerator,
|
||||
ObjectiveFunction,
|
||||
enumerator
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the type of value being compared against in a <see cref="OptionStrategyLegPredicate"/>.
|
||||
/// These values define the limits of what can be filtered and must match available slice methods in
|
||||
/// <see cref="OptionPositionCollection"/>
|
||||
/// </summary>
|
||||
public enum PredicateTargetValue
|
||||
{
|
||||
/// <summary>
|
||||
/// Predicate matches on <see cref="OptionPosition.Right"/> (0)
|
||||
/// </summary>
|
||||
Right,
|
||||
|
||||
/// <summary>
|
||||
/// Predicate match on <see cref="OptionPosition.Quantity"/> (1)
|
||||
/// </summary>
|
||||
Quantity,
|
||||
|
||||
/// <summary>
|
||||
/// Predicate matches on <see cref="OptionPosition.Strike"/> (2)
|
||||
/// </summary>
|
||||
Strike,
|
||||
|
||||
/// <summary>
|
||||
/// Predicate matches on <see cref="OptionPosition.Expiration"/> (3)
|
||||
/// </summary>
|
||||
Expiration
|
||||
}
|
||||
}
|
||||
+44
@@ -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.Orders;
|
||||
using System.Linq;
|
||||
|
||||
namespace QuantConnect.Securities.Option.StrategyMatcher
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides an implementation of <see cref="IOptionStrategyMatchObjectiveFunction"/> that evaluates the number of unmatched
|
||||
/// positions, in number of contracts, giving precedence to solutions that have fewer unmatched contracts.
|
||||
/// </summary>
|
||||
public class UnmatchedPositionCountOptionStrategyMatchObjectiveFunction : IOptionStrategyMatchObjectiveFunction
|
||||
{
|
||||
/// <summary>
|
||||
/// Computes the delta in matched vs unmatched positions, which gives precedence to solutions that match more contracts.
|
||||
/// </summary>
|
||||
public decimal ComputeScore(OptionPositionCollection input, OptionStrategyMatch match, OptionPositionCollection unmatched)
|
||||
{
|
||||
var value = 0m;
|
||||
foreach (var strategy in match.Strategies)
|
||||
{
|
||||
foreach (var leg in strategy.OptionLegs.Concat<Leg>(strategy.UnderlyingLegs))
|
||||
{
|
||||
value += leg.Quantity;
|
||||
}
|
||||
}
|
||||
|
||||
return value - unmatched.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user