mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-11-02 07:26:12 +00:00
Series arithmetic (lib/numerics): - Add, Sub, Mul, Div, Lag, reusing BiInputIndicatorBase at period=1 - Div publishes 0.0 (not NaN) on a zero denominator, since a non-finite ComputeError result permanently poisons the base's running sum - BiInputIndicatorBase gains Subscribe(a, b) implementing the strategy-primitives spec's time-join rule for event-chained two-input nodes Phase 0 contracts (lib/core): - TTradeContext, BracketFlags, ExitReason, TrendState, VolState - SignalConstants (DefaultEpsilon, TruthThreshold), OutOfRangePolicy - ConfirmOnClose: bar-confirmation wrapper (Section 8.6) Signal predicates (lib/signals/predicates), Phase 1: - Above, Below, Equal (ComparePredicateBase<TOp> generic comparison core) - InsideChannel, OutsideChannel, ChannelPosition (new MultiInputBase, 3-input time-join) - IsRising, IsFalling (exclude current bar, confirm-then-push window) - AtHighest, AtLowest (include current bar, rebuild-on-correction, mirrors channels/Dc) - Hysteresis, PercentDistance All new nodes ship with .cs, tests, and .md docs; indices and the StrategyPrimitives.Spec.md are updated with placement decisions and design notes. Full suite: 16730 tests, 0 failures, 0 warnings.
66 lines
2.5 KiB
C#
66 lines
2.5 KiB
C#
// INSIDECHANNEL: Channel Membership Predicate
|
|
// Crisp predicate: publishes 1.0 when lower <= value <= upper, 0.0 otherwise, NaN while cold.
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
namespace QuanTAlib;
|
|
|
|
/// <summary>
|
|
/// INSIDECHANNEL: Channel Membership Predicate
|
|
/// Tests whether a value is inside (inclusive) a pair of channel bounds.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Key properties:
|
|
/// - Crisp output: exactly 1.0 (true) or 0.0 (false) while hot, NaN while cold
|
|
/// - Bounds are inclusive: <c>lower <= value <= upper</c>
|
|
/// - Works with any three time-aligned streams, including the Upper/Lower outputs of any
|
|
/// existing channel indicator (Bollinger, Keltner, Donchian, ...)
|
|
/// - A fixed-scalar-bounds overload covers the common case of a fixed oscillator zone
|
|
/// (e.g. RSI between 30 and 70) without wiring two extra constant streams
|
|
/// - Built on <see cref="MultiInputBase"/> with 3 inputs (value, lower, upper)
|
|
/// </remarks>
|
|
/// <seealso href="InsideChannel.md">Detailed documentation</seealso>
|
|
[SkipLocalsInit]
|
|
public sealed class InsideChannel : MultiInputBase
|
|
{
|
|
private readonly double _scalarLower;
|
|
private readonly double _scalarUpper;
|
|
|
|
/// <summary>
|
|
/// Creates an InsideChannel predicate for direct pull-style updates.
|
|
/// </summary>
|
|
public InsideChannel() : base(3, "InsideChannel")
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an InsideChannel predicate chained to three upstream publishers (value, lower,
|
|
/// upper) using the time-join rule.
|
|
/// </summary>
|
|
public InsideChannel(ITValuePublisher value, ITValuePublisher lower, ITValuePublisher upper) : this()
|
|
{
|
|
Subscribe(value, lower, upper);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an InsideChannel predicate testing a stream against fixed scalar bounds.
|
|
/// </summary>
|
|
public InsideChannel(ITValuePublisher value, double lower, double upper) : this()
|
|
{
|
|
_scalarLower = lower;
|
|
_scalarUpper = upper;
|
|
value.Pub += HandleScalarUpdate;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void HandleScalarUpdate(object? sender, in TValueEventArgs e) =>
|
|
Update(e.Value, new TValue(e.Value.Time, _scalarLower), new TValue(e.Value.Time, _scalarUpper), e.IsNew);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
protected override double Compute(ReadOnlySpan<double> values)
|
|
{
|
|
double value = values[0], lower = values[1], upper = values[2];
|
|
return value >= lower && value <= upper ? 1.0 : 0.0;
|
|
}
|
|
}
|