/*
* 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 System.Collections.Generic;
namespace QuantConnect.Securities.Option.StrategyMatcher
{
///
/// Defines a match of to a
///
public class OptionStrategyDefinitionMatch : IEquatable
{
///
/// The matched
///
public OptionStrategyDefinition Definition { get; }
///
/// The number of times the definition is able to match the available positions.
/// Since definitions are formed at the 'unit' level, such as having 1 contract,
/// the multiplier defines how many times the definition matched. This multiplier
/// is used to scale the quantity defined in each leg definition when creating the
/// objects.
///
public int Multiplier { get; }
///
/// The instances matched to the definition.
///
public IReadOnlyList Legs { get; }
///
/// Initializes a new instance of the class
///
public OptionStrategyDefinitionMatch(
OptionStrategyDefinition definition,
IReadOnlyList legs,
int multiplier
)
{
Legs = legs;
Multiplier = multiplier;
Definition = definition;
}
///
/// Deducts the matched positions from the specified taking into account the multiplier
///
public OptionPositionCollection RemoveFrom(OptionPositionCollection positions)
{
var optionPositions = Legs.Select(leg => leg.CreateOptionPosition(Multiplier));
if (Definition.UnderlyingLots != 0)
{
optionPositions = optionPositions.Concat(new[]
{
new OptionPosition(Legs[0].Position.Symbol.Underlying, Definition.UnderlyingLots * Multiplier)
});
}
return positions.RemoveRange(optionPositions);
}
///
/// Creates the instance this match represents
///
public OptionStrategy CreateStrategy()
{
var legs = Legs.Select(leg => leg.CreateOptionStrategyLeg(Multiplier));
var strategy = new OptionStrategy {
Name = Definition.Name,
Underlying = Legs[0].Position.Underlying
};
foreach (var optionLeg in legs)
{
optionLeg.Invoke(strategy.UnderlyingLegs.Add, strategy.OptionLegs.Add);
}
if (Definition.UnderlyingLots != 0)
{
strategy.UnderlyingLegs = new List
{
OptionStrategy.UnderlyingLegData.Create(Definition.UnderlyingLots * Multiplier, Legs[0].Position.Underlying)
};
}
return strategy;
}
/// 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(OptionStrategyDefinitionMatch other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
if (!Equals(Definition, other.Definition))
{
return false;
}
// index legs by OptionPosition so we can do the equality while ignoring ordering
var positions = other.Legs.ToDictionary(leg => leg.Position, leg => leg.Multiplier);
foreach (var leg in other.Legs)
{
int multiplier;
if (!positions.TryGetValue(leg.Position, out multiplier))
{
return false;
}
if (leg.Multiplier != multiplier)
{
return false;
}
}
return true;
}
/// Determines whether the specified object is equal to the current object.
/// The object to compare with the current object.
/// true if the specified object is equal to the current object; otherwise, false.
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != GetType())
{
return false;
}
return Equals((OptionStrategyDefinitionMatch) obj);
}
/// Serves as the default hash function.
/// A hash code for the current object.
public override int GetHashCode()
{
unchecked
{
// we want to ensure that the ordering of legs does not impact equality operators in
// pursuit of this, we compute the hash codes of each leg, placing them into an array
// and then sort the array. using the sorted array, aggregates the hash codes
var hashCode = Definition.GetHashCode();
var arr = new int[Legs.Count];
for (int i = 0; i < Legs.Count; i++)
{
arr[i] = Legs[i].GetHashCode();
}
Array.Sort(arr);
for (int i = 0; i < arr.Length; i++)
{
hashCode = (hashCode * 397) ^ arr[i];
}
return hashCode;
}
}
/// Returns a string that represents the current object.
/// A string that represents the current object.
public override string ToString()
{
return $"{Definition.Name}: {string.Join("|", Legs.Select(leg => leg.Position))}";
}
///
/// OptionStrategyDefinitionMatch == Operator
///
/// True if they are the same
public static bool operator ==(OptionStrategyDefinitionMatch left, OptionStrategyDefinitionMatch right)
{
return Equals(left, right);
}
///
/// OptionStrategyDefinitionMatch != Operator
///
/// True if they are not the same
public static bool operator !=(OptionStrategyDefinitionMatch left, OptionStrategyDefinitionMatch right)
{
return !Equals(left, right);
}
}
}