/// <summary>
        /// Resolves the position groups that exist within the specified collection of positions.
        /// </summary>
        /// <param name="positions">The collection of positions</param>
        /// <returns>An enumerable of position groups</returns>
        public PositionGroupCollection Resolve(PositionCollection positions)
        {
            var result = new PositionGroupCollection(positions
                                                     .Select(position => new PositionGroup(_buyingPowerModel, position)).ToList()
                                                     );

            positions.Clear();
            return(result);
        }
        /// <summary>
        /// Merges this position group collection with the provided <paramref name="other"/> collection.
        /// </summary>
        public PositionGroupCollection CombineWith(PositionGroupCollection other)
        {
            var result = this;

            foreach (var positionGroup in other)
            {
                result = result.Add(positionGroup);
            }

            return(result);
        }
Example #3
0
 /// <summary>
 /// Resolves the algorithm's position groups from all of its holdings
 /// </summary>
 private void ResolvePositionGroups()
 {
     if (_requiresGroupResolution)
     {
         _requiresGroupResolution = false;
         // TODO : Replace w/ special IPosition impl to always equal security.Quantity and we'll
         // use them explicitly for resolution collection so we don't do this each time
         var investedPositions   = _securities.Where(kvp => kvp.Value.Invested).Select(kvp => (IPosition) new Position(kvp.Value));
         var positionsCollection = new PositionCollection(investedPositions);
         Groups = ResolvePositionGroups(positionsCollection);
     }
 }
        /// <summary>
        /// Attempts to group the specified positions into a new <see cref="IPositionGroup"/> using an
        /// appropriate <see cref="IPositionGroupBuyingPowerModel"/> for position groups created via this
        /// resolver.
        /// </summary>
        /// <param name="newPositions">The positions to be grouped</param>
        /// <param name="currentPositions">The currently grouped positions</param>
        /// <param name="group">The grouped positions when this resolver is able to, otherwise null</param>
        /// <returns>True if this resolver can group the specified positions, otherwise false</returns>
        public bool TryGroup(IReadOnlyCollection <IPosition> newPositions, PositionGroupCollection currentPositions, out IPositionGroup group)
        {
            foreach (var resolver in _resolvers)
            {
                if (resolver.TryGroup(newPositions, currentPositions, out group))
                {
                    return(true);
                }
            }

            group = null;
            return(false);
        }
        /// <summary>
        /// Attempts to group the specified positions into a new <see cref="IPositionGroup"/> using an
        /// appropriate <see cref="IPositionGroupBuyingPowerModel"/> for position groups created via this
        /// resolver.
        /// </summary>
        /// <param name="newPositions">The positions to be grouped</param>
        /// <param name="currentPositions">The currently grouped positions</param>
        /// <param name="group">The grouped positions when this resolver is able to, otherwise null</param>
        /// <returns>True if this resolver can group the specified positions, otherwise false</returns>
        public bool TryGroup(IReadOnlyCollection <IPosition> newPositions, PositionGroupCollection currentPositions, out IPositionGroup group)
        {
            // we can only create default groupings containing a single security
            if (newPositions.Count != 1)
            {
                group = null;
                return(false);
            }

            var key = new PositionGroupKey(_buyingPowerModel, newPositions);

            group = new PositionGroup(key, newPositions.ToDictionary(p => p.Symbol));
            return(true);
        }
 /// <summary>
 /// Determines the position groups that would be evaluated for grouping of the specified
 /// positions were passed into the <see cref="Resolve"/> method.
 /// </summary>
 /// <remarks>
 /// This function allows us to determine a set of impacted groups and run the resolver on just
 /// those groups in order to support what-if analysis
 /// </remarks>
 /// <param name="groups">The existing position groups</param>
 /// <param name="positions">The positions being changed</param>
 /// <returns>An enumerable containing the position groups that could be impacted by the specified position changes</returns>
 public IEnumerable <IPositionGroup> GetImpactedGroups(PositionGroupCollection groups, IReadOnlyCollection <IPosition> positions)
 {
     foreach (var resolver in _resolvers)
     {
         var seen = new HashSet <PositionGroupKey>();
         foreach (var group in resolver.GetImpactedGroups(groups, positions))
         {
             if (seen.Add(group.Key))
             {
                 yield return(group);
             }
         }
     }
 }
        /// <summary>
        /// Resolves the position groups that exist within the specified collection of positions.
        /// </summary>
        /// <param name="positions">The collection of positions</param>
        /// <returns>An enumerable of position groups</returns>
        public PositionGroupCollection Resolve(PositionCollection positions)
        {
            var result = PositionGroupCollection.Empty;

            var groups = GetPositionGroups(positions).ToList();

            if (groups.Count != 0)
            {
                result = new PositionGroupCollection(groups);

                // we are expected to remove any positions which we resolved into a position group
                positions.Remove(result);
            }

            return(result);
        }
        /// <summary>
        /// Attempts to group the specified positions into a new <see cref="IPositionGroup"/> using an
        /// appropriate <see cref="IPositionGroupBuyingPowerModel"/> for position groups created via this
        /// resolver.
        /// </summary>
        /// <param name="newPositions">The positions to be grouped</param>
        /// <param name="currentPositions">The currently grouped positions</param>
        /// <param name="group">The grouped positions when this resolver is able to, otherwise null</param>
        /// <returns>True if this resolver can group the specified positions, otherwise false</returns>
        public bool TryGroup(IReadOnlyCollection <IPosition> newPositions, PositionGroupCollection currentPositions, out IPositionGroup @group)
        {
            var impactedGroups = GetImpactedGroups(currentPositions, newPositions);
            var positionsToConsiderInNewGroup = impactedGroups.SelectMany(positionGroup => positionGroup.Positions);

            @group = GetPositionGroups(newPositions.Concat(positionsToConsiderInNewGroup)).Where(positionGroup =>
            {
                // from the resolved position groups we will take those which use our buying power model and which are related to the new positions to be executed
                if (positionGroup.BuyingPowerModel.GetType() == typeof(OptionStrategyPositionGroupBuyingPowerModel))
                {
                    return(newPositions.Any(position => positionGroup.TryGetPosition(position.Symbol, out position)));
                }
                return(false);
            }).FirstOrDefault();

            return(@group != null);
        }
Example #9
0
        /// <summary>
        /// Initializes a new instance of the <see cref="PositionManager"/> class
        /// </summary>
        /// <param name="securities">The algorithm's security manager</param>
        public PositionManager(SecurityManager securities)
        {
            _securities   = securities;
            Groups        = PositionGroupCollection.Empty;
            _defaultModel = new SecurityPositionGroupBuyingPowerModel();
            _resolver     = new CompositePositionGroupResolver(new OptionStrategyPositionGroupResolver(securities),
                                                               new SecurityPositionGroupResolver(_defaultModel));

            // we must be notified each time our holdings change, so each time a security is added, we
            // want to bind to its SecurityHolding.QuantityChanged event so we can trigger the resolver

            securities.CollectionChanged += (sender, args) =>
            {
                var items = args.NewItems ?? new List <object>();
                if (args.OldItems != null)
                {
                    foreach (var item in args.OldItems)
                    {
                        items.Add(item);
                    }
                }

                foreach (Security security in items)
                {
                    if (args.Action == NotifyCollectionChangedAction.Add)
                    {
                        security.Holdings.QuantityChanged += HoldingsOnQuantityChanged;
                        if (security.Invested)
                        {
                            // if this security has holdings then we'll need to resolve position groups
                            _requiresGroupResolution = true;
                        }
                    }
                    else if (args.Action == NotifyCollectionChangedAction.Remove)
                    {
                        security.Holdings.QuantityChanged -= HoldingsOnQuantityChanged;
                        if (security.Invested)
                        {
                            // only trigger group resolution if we had holdings in the removed security
                            _requiresGroupResolution = true;
                        }
                    }
                }
            };
        }
        /// <summary>
        /// Determines the position groups that would be evaluated for grouping of the specified
        /// positions were passed into the <see cref="Resolve"/> method.
        /// </summary>
        /// <remarks>
        /// This function allows us to determine a set of impacted groups and run the resolver on just
        /// those groups in order to support what-if analysis
        /// </remarks>
        /// <param name="groups">The existing position groups</param>
        /// <param name="positions">The positions being changed</param>
        /// <returns>An enumerable containing the position groups that could be impacted by the specified position changes</returns>
        public IEnumerable <IPositionGroup> GetImpactedGroups(PositionGroupCollection groups, IReadOnlyCollection <IPosition> positions)
        {
            var symbolsSet = positions.Where(position => position.Symbol.SecurityType.HasOptions() || position.Symbol.SecurityType.IsOption())
                             .SelectMany(position =>
            {
                return(position.Symbol.HasUnderlying ? new[] { position.Symbol, position.Symbol.Underlying } : new[] { position.Symbol });
            })
                             .ToHashSet();

            if (symbolsSet.Count == 0)
            {
                return(Enumerable.Empty <IPositionGroup>());
            }

            // will select groups for which we actually hold some security quantity and any of the changed symbols or underlying are in it if they are options
            return(groups.Where(group => group.Quantity != 0 &&
                                group.Positions.Any(position1 => symbolsSet.Contains(position1.Symbol) ||
                                                    position1.Symbol.HasUnderlying && position1.Symbol.SecurityType.IsOption() && symbolsSet.Contains(position1.Symbol.Underlying))));
        }
        /// <summary>
        /// Determines the position groups that would be evaluated for grouping of the specified
        /// positions were passed into the <see cref="IPositionGroupResolver.Resolve"/> method.
        /// </summary>
        /// <remarks>
        /// This function allows us to determine a set of impacted groups and run the resolver on just
        /// those groups in order to support what-if analysis
        /// </remarks>
        /// <param name="groups">The existing position groups</param>
        /// <param name="positions">The positions being changed</param>
        /// <returns>An enumerable containing the position groups that could be impacted by the specified position changes</returns>
        public IEnumerable <IPositionGroup> GetImpactedGroups(
            PositionGroupCollection groups,
            IReadOnlyCollection <IPosition> positions
            )
        {
            var seen = new HashSet <PositionGroupKey>();

            foreach (var position in positions)
            {
                IReadOnlyCollection <IPositionGroup> groupsForSymbol;
                if (!groups.TryGetGroups(position.Symbol, out groupsForSymbol))
                {
                    continue;
                }

                foreach (var group in groupsForSymbol)
                {
                    if (seen.Add(group.Key))
                    {
                        yield return(group);
                    }
                }
            }
        }