Files
quantconnect--lean/Common/Securities/Positions/PositionGroup.cs
T
2026-07-13 13:02:50 +08:00

143 lines
6.3 KiB
C#

/*
* 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.Linq;
using System.Collections;
using System.Collections.Generic;
using System;
using QuantConnect.Securities.Option;
namespace QuantConnect.Securities.Positions
{
/// <summary>
/// Provides a default implementation of <see cref="IPositionGroup"/>
/// </summary>
public class PositionGroup : IPositionGroup
{
/// <summary>
/// Gets the number of positions in the group
/// </summary>
public int Count => _positions.Count;
/// <summary>
/// Gets the key identifying this group
/// </summary>
public PositionGroupKey Key { get; }
/// <summary>
/// Gets the whole number of units in this position group
/// </summary>
public decimal Quantity { get; }
/// <summary>
/// Gets the positions in this group
/// </summary>
public IEnumerable<IPosition> Positions => _positions.Values;
/// <summary>
/// Gets the buying power model defining how margin works in this group
/// </summary>
public IPositionGroupBuyingPowerModel BuyingPowerModel => Key.BuyingPowerModel;
private readonly Dictionary<Symbol, IPosition> _positions;
/// <summary>
/// Initializes a new instance of the <see cref="PositionGroup"/> class
/// </summary>
/// <param name="buyingPowerModel">The buying power model to use for this group</param>
/// <param name="quantity">The group quantity, which must be the ratio of quantity to unit quantity of each position</param>
/// <param name="positions">The positions comprising this group</param>
/// <exception cref="ArgumentException">Thrown when the quantity is not the ratio of quantity to unit quantity of each position</exception>
public PositionGroup(IPositionGroupBuyingPowerModel buyingPowerModel, decimal quantity, params IPosition[] positions)
: this(new PositionGroupKey(buyingPowerModel, positions), quantity, positions.ToDictionary(p => p.Symbol))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PositionGroup"/> class
/// </summary>
/// <param name="key">The deterministic key for this group</param>
/// <param name="quantity">The group quantity, which must be the ratio of quantity to unit quantity of each position</param>
/// <param name="positions">The positions comprising this group</param>
/// <exception cref="ArgumentException">Thrown when the quantity is not the ratio of quantity to unit quantity of each position</exception>
public PositionGroup(PositionGroupKey key, decimal quantity, params IPosition[] positions)
: this(key, quantity, positions.ToDictionary(p => p.Symbol))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PositionGroup"/> class
/// </summary>
/// <param name="key">The deterministic key for this group</param>
/// <param name="quantity">The group quantity, which must be the ratio of quantity to unit quantity of each position</param>
/// <param name="positions">The positions comprising this group</param>
/// <exception cref="ArgumentException">Thrown when the quantity is not the ratio of quantity to unit quantity of each position</exception>
public PositionGroup(PositionGroupKey key, decimal quantity, Dictionary<Symbol, IPosition> positions)
{
Key = key;
Quantity = quantity;
_positions = positions;
#if DEBUG
if (positions.Any(kvp => Math.Abs(kvp.Value.Quantity / kvp.Value.UnitQuantity) != Math.Abs(Quantity)))
{
throw new ArgumentException(Messages.PositionGroup.InvalidQuantity(Quantity, positions.Values));
}
#endif
}
/// <summary>
/// Attempts to retrieve the position with the specified symbol
/// </summary>
/// <param name="symbol">The symbol</param>
/// <param name="position">The position, if found</param>
/// <returns>True if the position was found, otherwise false</returns>
public bool TryGetPosition(Symbol symbol, out IPosition position)
{
return _positions.TryGetValue(symbol, out position);
}
/// <summary>Returns a string that represents the current object.</summary>
/// <returns>A string that represents the current object.</returns>
public override string ToString()
{
return $"{Key}: {Quantity}";
}
/// <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<IPosition> GetEnumerator()
{
return Positions.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();
}
/// <summary>
/// Instantiates a default empty position group instance
/// </summary>
/// <param name="buyingPowerModel">The buying power model to use for this group</param>
public static PositionGroup Empty(IPositionGroupBuyingPowerModel buyingPowerModel)
{
return new PositionGroup(new PositionGroupKey(buyingPowerModel, new List<IPosition>()), 0m);
}
}
}