Complete thin Dx-composition wrapper indicators with full test coverage: - PlusDi/MinusDi: Directional Indicator wrappers (DiPlus/DiMinus from Dx) - PlusDm/MinusDm: Directional Movement wrappers (DmPlus/DmMinus from Dx) - Individual validation tests per indicator directory (TALib, Skender, bounds) - Combined unit tests (DiDm.Tests.cs) and validation tests (DiDm.Validation.Tests.cs) - Quantower wrappers + tests for all 4 indicators - PineScript v6 implementations with compensated RMA - Normalized .md documentation for all indicators and categories - 182 tests passing, 0 failures
8.7 KiB
PSL: Psychological Line
Markets are crowds, and crowds have moods. Count the up days; you will know the mood.
| Property | Value |
|---|---|
| Category | Oscillator |
| Inputs | Source (close) |
| Parameters | period (default 12) |
| Outputs | Single series (percentage of up-bars) |
| Output range | 0 to 100 |
| Warmup | period bars |
| PineScript | psl.pine |
Key takeaways
- Counts the percentage of bars where price closed higher than the previous bar over a lookback window.
- Output of
100means every bar in the window was an up-bar;0means every bar was a down-bar or unchanged. - A purely sentiment-driven indicator: it measures market psychology (bullish/bearish streak) rather than magnitude of moves.
- Uses a circular buffer storing
1.0(up) or0.0(down/unchanged) with a running sum for O(1) updates. - Readings above
75suggest excessive optimism; below25suggest excessive pessimism.
Historical Context
The Psychological Line (PSL) comes from the Japanese technical analysis tradition, where it has been used for decades as a simple gauge of market sentiment. The idea is rooted in crowd psychology: when too many consecutive bars close higher, the crowd is euphorically bullish and likely to reverse. When too many close lower, the crowd is excessively bearish and due for a bounce.
PSL is one of the simplest possible oscillators. It ignores the magnitude of price changes entirely, caring only about direction. A 0.01% gain counts the same as a 5% gain. This deliberate blindness to magnitude is the indicator's defining characteristic: it measures mood, not movement.
The indicator never gained wide adoption in Western technical analysis, where RSI and Stochastic dominate. But it fills a unique niche. No other standard oscillator measures purely the fraction of positive bars in a window.
What It Measures and Why It Matters
PSL answers one question: what percentage of the last N bars were up-bars? An up-bar is defined as one where the close exceeds the previous close. Down-bars and unchanged bars both count as "not up."
A PSL of 75 means three out of four recent bars closed higher. This does not tell you how much price rose, only that the direction was consistently up. The value lies in identifying streaks. Markets that have been consistently closing higher (or lower) without interruption tend to be overextended in that direction.
PSL is a contrarian indicator at extremes. When PSL exceeds 75, the market has been relentlessly bullish: historically, such streaks tend to exhaust themselves. When PSL drops below 25, the consistent selling may be running out of momentum. Between those thresholds, PSL provides less actionable information.
Mathematical Foundation
Core Formula
Define:
U_t = \begin{cases} 1 & \text{if } P_t > P_{t-1} \\ 0 & \text{otherwise} \end{cases}
\text{PSL}_t = 100 \times \frac{\sum_{i=0}^{N-1} U_{t-i}}{N}
where:
P_t= current price (close)N= lookback period (default 12)U_t= up-bar indicator (binary: 1 or 0)
Parameter Mapping
| Parameter | Symbol | Default | Constraint |
|---|---|---|---|
period |
N |
12 | N \geq 1 |
Warmup Period
W = N
The buffer must fill with N up/down classifications before the percentage is computed over the full window.
Architecture & Physics
1. Binary Circular Buffer
A RingBuffer of capacity N stores 1.0 (up-bar) or 0.0 (down/unchanged). A running sum (UpSum) counts the number of up-bars currently in the window. The percentage is simply 100 \times \text{UpSum} / \text{Count}.
2. Up-Bar Classification
The comparison value > PrevValue determines the binary classification. The first bar has no previous value, so it defaults to 0.0 (not an up-bar). PrevValue is tracked in the state struct.
3. State Management
A record struct State holds UpSum, PrevValue, LastValid, and Count. The _state / _p_state pattern supports bar correction via isNew flag.
4. Batch Path
Batch(ReadOnlySpan, Span, int) uses a local RingBuffer and running sum, producing identical results without indicator instantiation.
5. Edge Cases
| Condition | Behavior |
|---|---|
period <= 0 |
ArgumentException with nameof(period) |
NaN / Infinity input |
Substitutes last valid value |
| No previous value (first bar) | Classified as 0 (not up) |
| All bars identical | All classified as 0 (not up); PSL = 0 |
| During warmup | Percentage computed over available bars |
Interpretation and Signals
Signal Zones
| Zone | Condition | Interpretation |
|---|---|---|
| Excessive optimism | PSL > 75 | Market has been consistently closing higher; bullish exhaustion likely |
| Neutral | 25 - 75 | Mixed direction; no clear sentiment extreme |
| Excessive pessimism | PSL < 25 | Market has been consistently closing lower; bearish exhaustion likely |
Signal Patterns
- Overbought reversal: PSL rises above
75then drops back below. The streak of up-bars has broken, suggesting selling pressure is emerging. - Oversold reversal: PSL falls below
25then rises back above. The streak of down-bars has broken, suggesting buying interest is returning. - Divergence: Price making new highs while PSL is declining means the highs are being achieved with fewer consecutive up-bars, suggesting fatigue.
- Extreme readings: PSL of
100(every bar up) or0(every bar down) are rare and typically mark climactic moves.
Practical Notes
PSL works best on daily timeframes where the "close" concept is well-defined. On intraday data, the up-bar/down-bar classification becomes noisier and less meaningful. Combine PSL with a volatility indicator (ATR, Bollinger Width) to distinguish between genuine sentiment extremes and low-volatility drift where price ticks up marginally each bar without real conviction.
Related Indicators
- Stoch: Measures position within range rather than direction frequency.
- Willr: Williams %R, measures close relative to range; complementary to PSL's direction-counting approach.
- Er: Efficiency Ratio, measures directional efficiency but considers magnitude, not just direction.
Validation
| Library | Batch | Streaming | Span | Notes |
|---|---|---|---|---|
| TA-Lib | -- | -- | -- | No PSL function |
| Skender | -- | -- | -- | Not available |
| Tulip | -- | -- | -- | Not available |
| Ooples | -- | -- | -- | Not available |
Validated via internal consistency across all four API modes (batch, streaming, span, eventing).
Performance Profile
Key Optimizations
- O(1) streaming: Running sum of binary values avoids window re-counting each bar.
- Binary buffer: Stores only
1.0or0.0, minimizing computation per element. - Zero allocation: Pre-allocated
RingBufferandrecord struct State. - Aggressive inlining:
[MethodImpl(AggressiveInlining)]onUpdateandBatch.
Operation Count (Streaming Mode)
| Operation | Count per bar |
|---|---|
| Comparison | 1 (value > prevValue) |
| SUB | 1 (remove oldest from sum) |
| ADD | 1 (add newest to sum) |
| MUL | 1 (100 * upSum) |
| DIV | 1 (/ count) |
| NaN check | 1 |
| Total | ~6 ops |
Common Pitfalls
- Magnitude blindness: A PSL of
80does not mean the market rose significantly. It means 80% of bars were technically up-bars, even if each up-bar moved only a fraction of a point. Always check actual price change alongside PSL. - Unchanged bars count as "not up": If price closes exactly flat versus the prior bar, it counts as
0(not up). In illiquid instruments with many unchanged closes, PSL will structurally read lower. - First bar is always $0$: With no previous value to compare, the first bar is classified as "not up." This is a design choice, not a bug.
- Period sensitivity: Short periods (e.g., 5) make PSL jumpy. Each bar flips between 80 and 60 with a single direction change. The default 12 is a reasonable balance.
- Not a trend indicator: PSL of
70does not mean the trend is up. It means recent bars were mostly up. In a volatile range, you can have 70% up-bars while price goes nowhere. - Daily data is ideal: PSL was designed for daily charts where "up day" and "down day" are meaningful concepts. On tick data, the up/down classification becomes noise.
References
- Japanese Technical Analysis tradition, various sources.
- Colby, R. W. The Encyclopedia of Technical Market Indicators, 2nd ed. McGraw-Hill, 2003.
- Achelis, S. B. Technical Analysis from A to Z. McGraw-Hill, 2000.