/* * 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; namespace QuantConnect.Securities.Option.StrategyMatcher { /// /// Defines a lightweight structure representing a position in an option contract or underlying. /// This type is heavily utilized by the options strategy matcher and is the parameter type of /// option strategy definition predicates. Underlying quantities should be represented in lot sizes, /// which is equal to the quantity of shares divided by the contract's multiplier and then rounded /// down towards zero (truncate) /// public struct OptionPosition : IEquatable { /// /// Gets a new with zero /// public static OptionPosition Empty(Symbol symbol) => new OptionPosition(symbol, 0); /// /// Determines whether or not this position has any quantity /// public bool HasQuantity => Quantity != 0; /// /// Determines whether or not this position is for the underlying symbol /// public bool IsUnderlying => !Symbol.HasUnderlying; /// /// Number of contracts held, can be positive or negative /// public int Quantity { get; } /// /// Option contract symbol /// public Symbol Symbol { get; } /// /// Gets the underlying symbol. If this position represents the underlying, /// then this property is the same as the property /// public Symbol Underlying => IsUnderlying ? Symbol : Symbol.Underlying; /// /// Option contract expiration date /// public DateTime Expiration { get { if (Symbol.HasUnderlying) { return Symbol.ID.Date; } throw new InvalidOperationException($"{nameof(Expiration)} is not valid for underlying symbols: {Symbol}"); } } /// /// Option contract strike price /// public decimal Strike { get { if (Symbol.HasUnderlying) { return Symbol.ID.StrikePrice; } throw new InvalidOperationException($"{nameof(Strike)} is not valid for underlying symbols: {Symbol}"); } } /// /// Option contract right (put/call) /// public OptionRight Right { get { if (Symbol.HasUnderlying) { return Symbol.ID.OptionRight; } throw new InvalidOperationException($"{nameof(Right)} is not valid for underlying symbols: {Symbol}"); } } /// /// Gets whether this position is short/long/none /// public PositionSide Side => (PositionSide) Math.Sign(Quantity); /// /// Initializes a new instance of the structure /// /// The option contract symbol /// The number of contracts held public OptionPosition(Symbol symbol, int quantity) { Symbol = symbol; Quantity = quantity; } /// /// Creates a new instance with negative /// public OptionPosition Negate() { return new OptionPosition(Symbol, -Quantity); } /// /// Creates a new with this position's /// and the provided /// public OptionPosition WithQuantity(int quantity) { return new OptionPosition(Symbol, quantity); } /// Indicates whether the current object is equal to another object of the same type. /// An object to compare with this object. /// true if the current object is equal to the parameter; otherwise, false. public bool Equals(OptionPosition other) { return Equals(Symbol, other.Symbol) && Quantity == other.Quantity; } /// Indicates whether this instance and a specified object are equal. /// The object to compare with the current instance. /// true if and this instance are the same type and represent the same value; otherwise, false. public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) { return false; } if (obj.GetType() != GetType()) { return false; } return Equals((OptionPosition) obj); } /// Returns the hash code for this instance. /// A 32-bit signed integer that is the hash code for this instance. public override int GetHashCode() { unchecked { return ((Symbol != null ? Symbol.GetHashCode() : 0) * 397) ^ Quantity; } } /// Returns the fully qualified type name of this instance. /// The fully qualified type name. public override string ToString() { var s = Quantity == 1 ? "" : "s"; if (Symbol.HasUnderlying) { return $"{Quantity} {Right.ToLower()}{s} on {Symbol.Underlying.Value} at ${Strike} expiring on {Expiration:yyyy-MM-dd}"; } return $"{Quantity} share{s} of {Symbol.Value}"; } /// /// OptionPosition * Operator, will multiple quantity by given factor /// /// OptionPosition to operate on /// Factor to multiply by /// Resulting OptionPosition public static OptionPosition operator *(OptionPosition left, int factor) { return new OptionPosition(left.Symbol, factor * left.Quantity); } /// /// OptionPosition * Operator, will multiple quantity by given factor /// /// OptionPosition to operate on /// Factor to multiply by /// Resulting OptionPosition public static OptionPosition operator *(int factor, OptionPosition right) { return new OptionPosition(right.Symbol, factor * right.Quantity); } /// /// OptionPosition + Operator, will add quantities together if they are for the same symbol. /// /// Resulting OptionPosition public static OptionPosition operator +(OptionPosition left, OptionPosition right) { if (!Equals(left.Symbol, right.Symbol)) { if (left == default(OptionPosition)) { return right; } if (right == default(OptionPosition)) { return left; } throw new InvalidOperationException("Unable to add OptionPosition instances with different symbols"); } return new OptionPosition(left.Symbol, left.Quantity + right.Quantity); } /// /// OptionPosition - Operator, will subtract left - right quantities if they are for the same symbol. /// /// Resulting OptionPosition public static OptionPosition operator -(OptionPosition left, OptionPosition right) { if (!Equals(left.Symbol, right.Symbol)) { if (left == default(OptionPosition)) { // 0 - right return right.Negate(); } if (right == default(OptionPosition)) { // left - 0 return left; } throw new InvalidOperationException("Unable to subtract OptionPosition instances with different symbols"); } return new OptionPosition(left.Symbol, left.Quantity - right.Quantity); } /// /// Option Position == Operator /// /// True if they are the same public static bool operator ==(OptionPosition left, OptionPosition right) { return Equals(left, right); } /// /// Option Position != Operator /// /// True if they are not the same public static bool operator !=(OptionPosition left, OptionPosition right) { return !Equals(left, right); } } }