06-27-2018, 05:22 PM
Hey all,
I've really been enjoying Gekko lately and thought I would share a PSAR Indicator/Strat I wrote.
PSAR, by itself, is an awful strategy - particularly in consolidating markets. The included strategy is provided more as an example, and simply buys and sells on trend reversals. That said, PSAR is a great way to set dynamic stop-losses (as opposed to static values or percentage-based trailing stop-losses).
While I didn't specifically port the Tulip code, I did verify my indicator generates the same results. I also heavily commented the indicator so newer users can try to figure out what its doing. If you can improve my code, let me know!
PSAR.toml
strategies/PSAR.js
indicators/PSAR.js
I've really been enjoying Gekko lately and thought I would share a PSAR Indicator/Strat I wrote.
PSAR, by itself, is an awful strategy - particularly in consolidating markets. The included strategy is provided more as an example, and simply buys and sells on trend reversals. That said, PSAR is a great way to set dynamic stop-losses (as opposed to static values or percentage-based trailing stop-losses).
While I didn't specifically port the Tulip code, I did verify my indicator generates the same results. I also heavily commented the indicator so newer users can try to figure out what its doing. If you can improve my code, let me know!
PSAR.toml
Code:
acceleration = 0.02
maximum = 0.2
persistence = 2
strategies/PSAR.js
Code:
// PSAR strat
// PT 06/26/18
// let's create our own method
var method = {};
// prepare everything our method needs
method.init = function() {
this.name = 'PSAR';
this.inTrade = false;
this.requiredHistory = 100;
// define the indicators we need
this.addIndicator('psar', 'PSAR', this.settings);
};
// what happens on every new candle?
method.update = function(candle) {
// nothing!
};
method.log = function(candle) {
// nothing!
};
method.check = function() {
var psar = this.indicators.psar;
var trend = psar.trend;
var trendDuration = psar.duration;
// var psarResult = psar.psar;
//Sell Signal
if(trend == "down" && trendDuration >= this.settings.persistence && this.inTrade){
this.advice('short');
this.inTrade = false;
}
//Buy Signal
if(trend == "up" && trendDuration >= this.settings.persistence && !this.inTrade){
this.advice('long');
this.inTrade = true;
}
};
module.exports = method;
indicators/PSAR.js
Code:
//PSAR
//PT-06/26/2018
var Indicator = function(config) {
this.input = 'candle';
this.acceleration = config.acceleration;
this.accelerationStep = config.acceleration;
this.persistence = config.persistence;
this.maximum = config.maximum;
this.psar = null;
this.initPsar = null;
this.psarEpAcc = null;
this.extremePoint = null;
this.trend = null;
this.previousTrend = null;
this.duration = 0;
this.candleHistory = [];
};
Indicator.prototype.update = function(candle) {
this.candleHistory.push(candle);
if(this.candleHistory.length >= 3) {
//Define candles
// var candle = this.candleHistory[2];
var previousCandle = this.candleHistory[1];
var prevPreviousCandle = this.candleHistory[0];
//first run
if (this.psar === null) {
this.extremePoint = prevPreviousCandle.low;
this.psar = prevPreviousCandle.high;
this.previousTrend = 'down';
}
//PSAR EQUATION
this.psarEpAcc = (this.psar - this.extremePoint) * this.acceleration;
//PAST TREND LOGIC
if (this.previousTrend == 'down') {
//calculate initial psar
this.initPsar = Math.max((this.psar - this.psarEpAcc), previousCandle.high, prevPreviousCandle.high);
//calculate PSAR
this.psar = (candle.high < this.initPsar) ? this.initPsar : this.extremePoint;
//increase duration count
this.duration++;
}
if (this.previousTrend == 'up') {
//calculate initial psar
this.initPsar = Math.min((this.psar - this.psarEpAcc), previousCandle.low, prevPreviousCandle.low);
//calculate PSAR
this.psar = (candle.low > this.initPsar) ? this.initPsar : this.extremePoint;
//increase duration count
this.duration++;
}
//Define trend
this.trend = (this.psar > candle.close) ? 'down' : 'up';
//CURRENT TREND LOGIC
if(this.trend == 'down'){
//calculate Extreme point
this.extremePoint = Math.min(this.extremePoint, candle.low);
}
if(this.trend =='up'){
//calculate Extreme point
this.extremePoint = Math.max(this.extremePoint, candle.high);
}
//UPDATE LOGIC
//If the trend stays the same and the extreme point doesn't equal the previous extreme point and the acceleration factor is less than the maximum factor...
if (this.trend == this.previousTrend && this.extremePoint != this.previousExtremePoint && this.acceleration <= this.maximum) {
//Increment the acceleration value
this.acceleration += this.accelerationStep;
}
//If the trend stays the same and the extreme point value stays the same...
if (this.trend == this.previousTrend && this.extremePoint == this.previousExtremePoint) {
//Acceleration stays the same
}
// If the current trend doesn't equal the previous trend...
if (this.trend != this.previousTrend) {
//Set the acceleration to the initial value
this.acceleration = this.accelerationStep;
//reset duration count
this.duration = 0;
}
//set values/array for next run
this.previousExtremePoint = this.extremePoint;
this.previousTrend = this.trend;
this.candleHistory.shift();
}
};
module.exports = Indicator;