chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user