/*
* 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);
}
}
}