// 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));
        }
示例#2
0
        /// <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);
        }
示例#3
0
        /// <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>
        /// 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));
        }
示例#5
0
        /// <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 <OptionStrategy.LegData>(strategy.UnderlyingLegs))
                {
                    value += leg.Quantity;
                }
            }

            return(value - unmatched.Count);
        }
示例#6
0
 /// <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>
        /// 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.");
        }
        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>
        /// 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());
        }
示例#10
0
 public IEnumerable <OptionPosition> Enumerate(OptionPositionCollection positions)
 {
     return(_enumerate(positions));
 }
示例#11
0
 /// <summary>
 /// Deducts the matched positions from the specified <paramref name="positions"/>
 /// </summary>
 public OptionPositionCollection RemoveFrom(OptionPositionCollection positions)
 {
     return(positions.RemoveRange(Legs.Select(leg => leg.Position)));
 }
 /// <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>
 /// 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);
 }