|
| 1 | +# Plotting Custom Indicators |
| 2 | + |
| 3 | +Once you've created a custom indicator, you'll want to see it on the results chart to analyze its behavior. Stochastix provides a clean way for an indicator to define its own default plotting configuration. This makes your custom indicators reusable and easy to visualize without adding complex plotting logic to your strategies. |
| 4 | + |
| 5 | +This is achieved by implementing the `getPlotDefinition()` method in your indicator class. |
| 6 | + |
| 7 | +## The `getPlotDefinition()` Method |
| 8 | + |
| 9 | +By implementing this method from the `IndicatorInterface`, your custom indicator can return a `PlotDefinition` object that acts as a "template" for how it should be drawn on a chart. |
| 10 | + |
| 11 | +The backtesting framework can then use this template automatically whenever you add the indicator to a strategy. |
| 12 | + |
| 13 | +## Implementing the Plot for `RSIMovingAverage` |
| 14 | + |
| 15 | +Let's continue with our `RSIMovingAverage` example and implement its `getPlotDefinition()` method. We want to display the RSI and its moving average in a separate pane below the price chart, with horizontal lines at the 70 and 30 levels. |
| 16 | + |
| 17 | +```php |
| 18 | +// In src/Indicator/RSIMovingAverage.php |
| 19 | + |
| 20 | +use Stochastix\Domain\Plot\Annotation\HorizontalLine; |
| 21 | +use Stochastix\Domain\Plot\Enum\HorizontalLineStyleEnum; |
| 22 | +use Stochastix\Domain\Plot\PlotDefinition; |
| 23 | +use Stochastix\Domain\Plot\Series\Line; |
| 24 | + |
| 25 | +// ... inside the RSIMovingAverage class |
| 26 | + |
| 27 | +public function getPlotDefinition(): ?PlotDefinition |
| 28 | +{ |
| 29 | + return new PlotDefinition( |
| 30 | + // This name is a default suggestion; it can be overridden by the strategy. |
| 31 | + name: 'RSI / MA', |
| 32 | + |
| 33 | + // 'false' renders the plot in a separate pane below the price chart. |
| 34 | + overlay: false, |
| 35 | + |
| 36 | + // Define the data series to draw. |
| 37 | + plots: [ |
| 38 | + // Draw a line using the data from the 'rsi' series key. |
| 39 | + new Line(key: 'rsi', color: '#4e79a7'), |
| 40 | + |
| 41 | + // Draw a second line using data from the 'rsi_ma' series key. |
| 42 | + new Line(key: 'rsi_ma', color: '#f28e2b'), |
| 43 | + ], |
| 44 | + |
| 45 | + // Define static annotations for the plot pane. |
| 46 | + annotations: [ |
| 47 | + new HorizontalLine(value: 70, style: HorizontalLineStyleEnum::Dashed), |
| 48 | + new HorizontalLine(value: 30, style: HorizontalLineStyleEnum::Dashed), |
| 49 | + ] |
| 50 | + ); |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +**Key Concepts from this step:** |
| 55 | +* **`plots` array**: We define one `Line` for each data series we stored in `$this->resultSeries`. The `key` in the `Line` constructor **must match** the key used in `calculateBatch()` (e.g., `'rsi'`, `'rsi_ma'`). |
| 56 | +* **`annotations` array**: We add static `HorizontalLine` components to provide context for the overbought (70) and oversold (30) RSI levels. |
| 57 | +* **`overlay: false`**: This is critical for oscillators like RSI, ensuring they get their own pane and don't get drawn on top of the price candles. |
| 58 | + |
| 59 | +## Using the Self-Plotting Indicator |
| 60 | + |
| 61 | +Now that our `RSIMovingAverage` indicator knows how to draw itself, using it in a strategy becomes incredibly simple. |
| 62 | + |
| 63 | +To activate the automatic plotting, you provide a third argument to the `$this->addIndicator()` method: the desired name for the plot in the chart legend. When this third argument is present, the framework automatically calls your indicator's `getPlotDefinition()` method and uses the template it provides. |
| 64 | + |
| 65 | +```php |
| 66 | +// In your strategy's defineIndicators() method: |
| 67 | + |
| 68 | +use App\Indicator\RSIMovingAverage; |
| 69 | + |
| 70 | +protected function defineIndicators(): void |
| 71 | +{ |
| 72 | + $this->addIndicator( |
| 73 | + 'my_rsi', // The key for the indicator instance |
| 74 | + new RSIMovingAverage(rsiPeriod: 14, maPeriod: 9), |
| 75 | + 'RSI (14) with MA (9)' // This 3rd argument activates the plot! |
| 76 | + ); |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +That's it! With this single line, you have added your custom indicator's logic to the backtest *and* configured it to be displayed beautifully on the results chart, with no plotting code needed in the strategy itself. |
0 commit comments