public void MatchesAgainstFullPositionCollection() { // sort definitions by leg count so that we match more complex definitions first var options = OptionStrategyMatcherOptions.ForDefinitions(OptionStrategyDefinitions.AllDefinitions .OrderByDescending(definition => definition.LegCount) ); var matcher = new OptionStrategyMatcher(options); var positions = OptionPositionCollection.Empty.AddRange(Option.Position(Option.Underlying, +20), Option.Position(Option.Call[100, -4]), Option.Position(Option.Put[105, -4]), Option.Position(Option.Call[105, +4]), Option.Position(Option.Put[110, +4]), Option.Position(Option.Call[110, -3]), Option.Position(Option.Put[115, -3]), Option.Position(Option.Call[115, +3]), Option.Position(Option.Put[120, +3]), Option.Position(Option.Call[120, -5]), Option.Position(Option.Put[125, -5]), Option.Position(Option.Call[124, +5]), Option.Position(Option.Put[130, +5]) ); var match = matcher.MatchOnce(positions); foreach (var strategy in match.Strategies) { Console.WriteLine($"{strategy.Name}"); foreach (var leg in strategy.OptionLegs) { // steal OptionPosition's ToString() implementation Console.Write($"\t{new OptionPosition(leg.Symbol, leg.Quantity)}"); } } }
public void DoesNotDoubleCountPositions() { // this test aims to verify that match solutions do not reference the same position in multiple matches // this behavior is different than the OptionStrategyDefinition.Match, which by design produces all possible // matches which permits the same position to appear in different matches, allowing the matcher to pick matches // this test aims to verify that we can match the same definition multiple times if positions allows // 0: -C110 +C105 // 1: -C115 +C100 var positions = OptionPositionCollection.Empty.AddRange( Position(Call[110], -1), Position(Call[115], -1), Position(Call[100]), Position(Call[105]) ); var matcher = new OptionStrategyMatcher(OptionStrategyMatcherOptions.ForDefinitions(BearCallSpread)); var matches = matcher.MatchOnce(positions); Assert.AreEqual(2, matches.Strategies.Count); }
public void CoveredCall_MatchesMultipleTimes_ForEachUniqueShortCallContract() { // CoveredCall // 0: +1 underlying lot // 1: -1 Call // so we should match // (4U, -4C100) // (3U, -3C110) // (5U, -5C120) // (9U, -9C130) // (20U, -20C140) // OptionStrategyDefinition.Match produces ALL possible matches var positions = Empty.AddRange(Position(Underlying, +20), Position(Call[100], -4), Position(Put[105], -4), Position(Call[105], +4), Position(Put[110], +4), Position(Call[110], -3), Position(Put[115], -3), Position(Call[115], +3), Position(Put[120], +3), Position(Call[120], -5), Position(Put[125], -5), Position(Call[125], +5), Position(Put[130], +5), Position(Call[130], -9), Position(Put[135], -9), Position(Call[140], -21), Position(Put[145], -21) ); // force lower strikes to be evaluated first to provide determinism for this test var options = OptionStrategyMatcherOptions.ForDefinitions(CoveredCall) .WithPositionEnumerator(new FunctionalOptionPositionCollectionEnumerator( pos => pos.OrderBy(p => p.IsUnderlying ? 0 : p.Strike) )); var matches = CoveredCall.Match(options, positions).ToList(); Assert.AreEqual(5, matches.Count); Assert.AreEqual(1, matches.Count(m => m.Multiplier == 4)); Assert.AreEqual(1, matches.Count(m => m.Multiplier == 3)); Assert.AreEqual(1, matches.Count(m => m.Multiplier == 5)); Assert.AreEqual(1, matches.Count(m => m.Multiplier == 9)); Assert.AreEqual(1, matches.Count(m => m.Multiplier == 20)); }
/// <summary> /// Creates the default option strategy group resolver for <see cref="OptionStrategyDefinitions.AllDefinitions"/> /// </summary> public OptionStrategyPositionGroupResolver(SecurityManager securities) : this(securities, OptionStrategyMatcherOptions.ForDefinitions(OptionStrategyDefinitions.AllDefinitions)) { }