The P-Signal multi timeframe trading strategy is a cryptocurrency algorithmic trading strategy based on statistical principles and multi timeframe analysis. The strategy uses the Gaussian error function and P-Signal indicator to model fit Bitcoin’s daily, weekly and monthly charts, and goes long on indicator crosses above 0 and exits on crosses below 0, in order to trade volatility.
The core indicator of the P-Signal strategy is the P-Signal itself, which combines statistical standard deviation and simple moving average, and maps it to the -1 to 1 range using the Gaussian error function, to detect whether the market conforms to the normal distribution. The specific calculation formula is as follows:
fErf(x) = 1.0 - 1.0/(1.0 + 0.5*abs(x)) * exp(-x*x - 1.26551223 + ...) # Gaussian error function
fPSignal(ser, n) = fErf((stdev(ser, n) > 0 ? sma(ser, n)/stdev(ser, n)/sqrt(2) : 1)) # P-Signal indicator
The strategy calculates the P-Signal indicator on the daily, weekly and monthly timeframes for Bitcoin, goes long when the indicator crosses above 0, and exits when it crosses back below 0. Indicator value valves are also set to control repeated entries.
The biggest advantage of the P-Signal strategy is the use of multiple timeframes to improve strategy stability. The daily chart captures short-term market fluctuations, while the weekly and monthly charts filter false breakouts. At the same time, the P-Signal indicator itself also has some predictive capability to amplify the fluctuations of trending moves.
Compared to a single timeframe, multiple timeframes allow the use of daily stops to control drawdown during volatile times, while reducing transaction frequency using the higher timeframes during ranging markets. Overall, this combination allows maximizing profits while minimizing both absolute and relative drawdowns.
The biggest risk of the P-Signal strategy is that the indicator itself is a black box to quant traders. We have no way of determining the adaptability of this indicator to specific markets, nor can we determine the optimal range of its parameters. This may lead to poor performance of the strategy in live trading.
In addition, the strategy itself has some limitations. For example, inability to handle violent moves, lagging signal from indicator crossover etc. All these can become hidden troubles during live trading.
To solve these issues, we can adjust indicator parameters, optimize stop loss, introduce more auxiliary indicators etc. But the premise is to test stability across large enough backtesting periods.
There are several directions to optimize the P-Signal strategy:
Change P-Signal indicator parameters: nIntr_D, nIntr_W and nIntr_M, find optimal parameter combinations
Add stop loss methods: trailing stop loss, time stop loss, ATR stop loss etc, find optimal stop loss
Introduce auxiliary indicators: improve judgment of specific market conditions, e.g. use MACD to determine trends
Optimize position sizing: set dynamic position sizing based on account usage efficiency
Machine learning optimization: use neural networks, genetic algorithms to find globally optimal parameters
The P-Signal multi timeframe trading strategy is overall a very promising strategy idea. It combines statistical principles and technical indicators, and uses multi timeframe analysis to improve stability. If we can solve some limitations through extensive backtesting and optimization, it is entirely possible to transform it into a real, usable cryptocurrency algorithmic trading strategy.
/*backtest start: 2022-11-21 00:00:00 end: 2023-11-27 00:00:00 period: 1d basePeriod: 1h exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}] */ // ********************************************************************************************************** // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // P-Signal Strategy © Kharevsky // A good strategy should be able to handle backtesting. // @version=4 // ********************************************************************************************************** strategy("P-Signal Strategy:", precision = 3, pyramiding = 3) // // Parameters and const of P-Signal. // nPoints_D = input(title = "Number of D Bars", type = input.integer, defval = 9, minval = 4, maxval = 100, group = "Parameters of observation.") nPoints_W = input(title = "Number of W Bars", type = input.integer, defval = 4, minval = 4, maxval = 100, group = "Parameters of observation.") nPoints_M = input(title = "Number of M Bars", type = input.integer, defval = 6, minval = 4, maxval = 100, group = "Parameters of observation.") int nIntr_D = nPoints_D - 1 int nIntr_W = nPoints_W - 1 int nIntr_M = nPoints_M - 1 bool bDValveOpen = true bool bWValveOpen = true bool bMValveOpen = true // // Horner's method for the error (Gauss) & P-Signal functions. // fErf(x) => nT = 1.0/(1.0 + 0.5*abs(x)) nAns = 1.0 - nT*exp(-x*x - 1.26551223 + nT*( 1.00002368 + nT*( 0.37409196 + nT*( 0.09678418 + nT*(-0.18628806 + nT*( 0.27886807 + nT*(-1.13520398 + nT*( 1.48851587 + nT*(-0.82215223 + nT*( 0.17087277 )))))))))) x >= 0 ? nAns : -nAns fPSignal(ser, int) => nStDev = stdev(ser, int) nSma = sma(ser, int) fErf(nStDev > 0 ? nSma/nStDev/sqrt(2) : 1.0) // // Signals for the strategy. // float nPSignal_D = sma(fPSignal(change(ohlc4), nIntr_D), nIntr_D) float ndPSignal_D = sign(nPSignal_D[0] - nPSignal_D[1]) // float nPSignal_W = sma(security(syminfo.tickerid, "W",fPSignal(change(ohlc4), nIntr_W)), nIntr_W) float ndPSignal_W = sign(nPSignal_W[0] - nPSignal_W[1]) // float nPSignal_M = sma(security(syminfo.tickerid, "M",fPSignal(change(ohlc4), nIntr_M)), nIntr_M) float ndPSignal_M = sign(nPSignal_M[0] - nPSignal_M[1]) // // P-Signal plotting. // hline(+1.0, color = color.new(color.orange,70), linestyle = hline.style_dotted) hline(-1.0, color = color.new(color.orange,70), linestyle = hline.style_dotted) plot(nPSignal_D, color = color.blue, style = plot.style_line) // // Multi Frame Strategy // ... Day if(nPSignal_D < 0 and ndPSignal_D > 0 and bDValveOpen) strategy.entry("long_D", strategy.long, 1) bDValveOpen := false if(nPSignal_D > 0 and ndPSignal_D < 0) strategy.close("long_D") bDValveOpen := true // ... Week if(nPSignal_W < 0 and ndPSignal_W > 0 and bWValveOpen) strategy.entry("long_W", strategy.long, 1) bWValveOpen := false if(nPSignal_W > 0 and ndPSignal_W < 0) strategy.close("long_W") bWValveOpen := true // ... Month if(nPSignal_M < 0 and ndPSignal_M > 0 and bMValveOpen) strategy.entry("long_M", strategy.long, 1) bMValveOpen := false if(nPSignal_M > 0 and ndPSignal_M < 0) strategy.close("long_M") bMValveOpen := true // The end.template: strategy.tpl:40:21: executing "strategy.tpl" at <.api.GetStrategyListByName>: wrong number of args for GetStrategyListByName: want 7 got 6