/* * 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.Expressions; using static QuantConnect.Util.ExpressionBuilder; namespace QuantConnect { /// /// Enumeration class defining binary comparisons and providing access to expressions and functions /// capable of evaluating a particular comparison for any type. If a particular type does not implement /// a binary comparison than an exception will be thrown. /// public class BinaryComparison { /// /// Gets the equivalent of /// public static readonly BinaryComparison Equal = new BinaryComparison(ExpressionType.Equal); /// /// Gets the equivalent of /// public static readonly BinaryComparison NotEqual = new BinaryComparison(ExpressionType.NotEqual); /// /// Gets the equivalent of /// public static readonly BinaryComparison LessThan = new BinaryComparison(ExpressionType.LessThan); /// /// Gets the equivalent of /// public static readonly BinaryComparison GreaterThan = new BinaryComparison(ExpressionType.GreaterThan); /// /// Gets the equivalent of /// public static readonly BinaryComparison LessThanOrEqual = new BinaryComparison(ExpressionType.LessThanOrEqual); /// /// Gets the equivalent of /// public static readonly BinaryComparison GreaterThanOrEqual = new BinaryComparison(ExpressionType.GreaterThanOrEqual); /// /// Gets the matching the provided /// public static BinaryComparison FromExpressionType(ExpressionType type) { switch (type) { case ExpressionType.Equal: return Equal; case ExpressionType.NotEqual: return NotEqual; case ExpressionType.LessThan: return LessThan; case ExpressionType.LessThanOrEqual: return LessThanOrEqual; case ExpressionType.GreaterThan: return GreaterThan; case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqual; default: throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison."); } } /// /// Gets the expression type defining the binary comparison. /// public ExpressionType Type { get; } private BinaryComparison(ExpressionType type) { Type = type; } /// /// Evaluates the specified and according to this /// public bool Evaluate(T left, T right) => OfType.GetFunc(Type)(left, right); /// /// Gets a function capable of performing this /// public Func GetEvaluator() => OfType.GetFunc(Type); /// /// Gets an expression representing this /// public Expression> GetExpression() => OfType.GetExpression(Type); /// /// Flips the logic ordering of the comparison's operands. For example, /// is converted into /// public BinaryComparison FlipOperands() { switch (Type) { case ExpressionType.Equal: return this; case ExpressionType.NotEqual: return this; case ExpressionType.LessThan: return GreaterThan; case ExpressionType.LessThanOrEqual: return GreaterThanOrEqual; case ExpressionType.GreaterThan: return LessThan; case ExpressionType.GreaterThanOrEqual: return LessThanOrEqual; default: throw new Exception( "The skies are falling and the oceans are rising! " + "If you've made it here then this exception is the least of your worries! " + $"ExpressionType: {Type}" ); } } /// Returns a string that represents the current object. /// A string that represents the current object. public override string ToString() { return Type.ToString(); } /// /// Provides thread-safe lookups of expressions and functions for binary comparisons by type. /// MUCH faster than using a concurrency dictionary, for example, as it's expanded at runtime /// and hard-linked, no look-up is actually performed! /// private static class OfType { private static readonly Expression> EqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.Equal); private static readonly Expression> NotEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.NotEqual); private static readonly Expression> LessThanExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.LessThan); private static readonly Expression> LessThanOrEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.LessThanOrEqual); private static readonly Expression> GreaterThanExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.GreaterThan); private static readonly Expression> GreaterThanOrEqualExpr = MakeBinaryComparisonLambdaOrNull(ExpressionType.GreaterThanOrEqual); public static Expression> GetExpression(ExpressionType type) { switch (type) { case ExpressionType.Equal: return EqualExpr; case ExpressionType.NotEqual: return NotEqualExpr; case ExpressionType.LessThan: return LessThanExpr; case ExpressionType.LessThanOrEqual: return LessThanOrEqualExpr; case ExpressionType.GreaterThan: return GreaterThanExpr; case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqualExpr; default: throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison."); } } private static readonly Func EqualFunc = EqualExpr?.Compile(); private static readonly Func NotEqualFunc = NotEqualExpr?.Compile(); private static readonly Func LessThanFunc = LessThanExpr?.Compile(); private static readonly Func LessThanOrEqualFunc = LessThanOrEqualExpr?.Compile(); private static readonly Func GreaterThanFunc = GreaterThanExpr?.Compile(); private static readonly Func GreaterThanOrEqualFunc = GreaterThanOrEqualExpr?.Compile(); public static Func GetFunc(ExpressionType type) { switch (type) { case ExpressionType.Equal: return EqualFunc; case ExpressionType.NotEqual: return NotEqualFunc; case ExpressionType.LessThan: return LessThanFunc; case ExpressionType.LessThanOrEqual: return LessThanOrEqualFunc; case ExpressionType.GreaterThan: return GreaterThanFunc; case ExpressionType.GreaterThanOrEqual: return GreaterThanOrEqualFunc; default: throw new InvalidOperationException($"The specified ExpressionType '{type}' is not a binary comparison."); } } private static Expression> MakeBinaryComparisonLambdaOrNull(ExpressionType type) { try { return MakeBinaryComparisonLambda(type); } catch { return null; } } } } }