/// <summary> /// Creates a new <see cref="OptionStrategyLegPredicate"/> from the specified predicate <paramref name="expression"/> /// </summary> public static OptionStrategyLegPredicate Create( Expression <Func <IReadOnlyList <OptionPosition>, OptionPosition, bool> > expression ) { // expr must NOT include compound comparisons // expr is a lambda of one of the following forms: // (legs, position) => position.{target} {comparison} legs[i].{reference-target} // (legs, position) => legs[i].{reference-target} {comparison} position.{target} // (legs, position) => position.{target} {comparison} {literal-reference-target} // (legs, position) => {literal-reference-target} {comparison} position.{target} // we want to make the comparison of a common form, specifically: // position.{target} {comparison} {reference-target} // this is so when we invoke OptionPositionCollection we have the correct comparison type // for example, legs[0].Strike > position.Strike // needs to be inverted into position.Strike < legs[0].Strike // so we can call OptionPositionCollection.Slice(BinaryComparison.LessThan, legs[0].Strike); try { var legsParameter = expression.Parameters[0]; var positionParameter = expression.Parameters[1]; var binary = expression.OfType <BinaryExpression>().Single(e => e.NodeType.IsBinaryComparison()); var comparison = BinaryComparison.FromExpressionType(binary.NodeType); var leftReference = CreateReferenceValue(legsParameter, positionParameter, binary.Left); var rightReference = CreateReferenceValue(legsParameter, positionParameter, binary.Right); if (leftReference != null && rightReference != null) { throw new ArgumentException($"The provided expression is not of the required form: {expression}"); } // we want the left side to be null, indicating position.{target} // if not, then we need to flip the comparison operand var reference = rightReference; if (rightReference == null) { reference = leftReference; comparison = comparison.FlipOperands(); } return(new OptionStrategyLegPredicate(comparison, reference, expression.Compile(), expression)); } catch { // we can still handle arbitrary predicates, they just require a full search of the positions // as we're unable to leverage any of the pre-build indexes via Slice methods. return(new OptionStrategyLegPredicate(null, null, expression.Compile(), expression)); } }
public void EvaluatesFlippedOperandsComparison(ExpressionType type, bool expected1, bool expected2) { const int left1 = 1; const int right1 = 1; const int left2 = 2; const int right2 = 3; var comparison = BinaryComparison.FromExpressionType(type).FlipOperands(); var actual1 = comparison.Evaluate(left1, right1); Assert.AreEqual(expected1, actual1); var actual2 = comparison.Evaluate(left2, right2); Assert.AreEqual(expected2, actual2); }