Browse Source

Added SHORT TRADING backtesting feature

develop
mark-sch 8 months ago
parent
commit
ddc2b2c532
7 changed files with 186 additions and 38 deletions
  1. +4
    -0
      core/pluginUtil.js
  2. +1
    -1
      package-lock.json
  3. +1
    -1
      package.json
  4. +58
    -19
      plugins/paperTrader/paperTrader.js
  5. +39
    -10
      plugins/performanceAnalyzer/logger.js
  6. +76
    -2
      plugins/performanceAnalyzer/performanceAnalyzer.js
  7. +7
    -5
      sample-eth.js

+ 4
- 0
core/pluginUtil.js View File

@ -3,6 +3,7 @@ var async = require('async');
var Emitter = require('./emitter');
var util = require(__dirname + '/util');
const colors = require('colors/safe');
var log = require(util.dirs().core + 'log');
@ -94,6 +95,9 @@ var pluginHelper = {
log.info('Setting up:');
log.info('\t', plugin.name);
log.info('\t', plugin.description);
if (plugin.config.shortTrading) {
log.info('\t', colors.red('Using SHORT TRADING performance analyzer'));
}
var cannotLoad = pluginHelper.cannotLoad(plugin);
if(cannotLoad)

+ 1
- 1
package-lock.json View File

@ -1,6 +1,6 @@
{
"name": "gekko",
"version": "2020.0.68.289",
"version": "2021.0.68.297",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

+ 1
- 1
package.json View File

@ -1,6 +1,6 @@
{
"name": "gekko",
"version": "2020.0.68.289",
"version": "2021.0.68.297",
"description": "A crypto trading bot for auto trading at various exchanges",
"keywords": [
"trading",

+ 58
- 19
plugins/paperTrader/paperTrader.js View File

@ -7,6 +7,7 @@ const ENV = util.gekkoEnv();
const config = util.getConfig();
const calcConfig = config.paperTrader;
const watchConfig = config.watch;
const shortTrading = config.performanceAnalyzer.shortTrading;
const dirs = util.dirs();
const log = require(dirs.core + 'log');
@ -33,9 +34,12 @@ const PaperTrader = function() {
this.balance = false;
if(this.portfolio.asset > 0) {
if(this.portfolio.asset > 0 && !shortTrading) {
this.exposed = true;
}
else {
this.exposed = false;
}
this.propogatedTrades = 0;
this.propogatedTriggers = 0;
@ -78,28 +82,63 @@ PaperTrader.prototype.updatePosition = function(what) {
let cost;
let amount;
// virtually trade all {currency} to {asset}
// at the current price (minus fees)
if(what === 'long') {
cost = (1 - this.fee) * this.portfolio.currency;
this.portfolio.asset += this.extractFee(this.portfolio.currency / this.price);
amount = this.portfolio.asset;
this.portfolio.currency = 0;
if (!shortTrading) {
// virtually trade all {currency} to {asset}
// at the current price (minus fees)
if(what === 'long') {
cost = (1 - this.fee) * this.portfolio.currency;
this.portfolio.asset += this.extractFee(this.portfolio.currency / this.price);
amount = this.portfolio.asset;
this.portfolio.currency = 0;
this.exposed = true;
this.trades++;
}
// virtually trade all {asset} to {currency}
// at the current price (minus fees)
else if(what === 'short') {
cost = (1 - this.fee) * (this.portfolio.asset * this.price);
amount = this.portfolio.asset;
this.portfolio.currency += this.extractFee(this.portfolio.asset * this.price);
this.portfolio.asset = 0;
this.exposed = true;
this.trades++;
this.exposed = false;
this.trades++;
}
}
else {
if(what === 'long') { //actually a closing short
if (this.portfolio.asset == 0) {
this.portfolio.asset = this.portfolio.currency / this.price;
this.portfolio.previouscurrency = this.portfolio.currency;
this.portfolio.currency = 0;
}
// virtually trade all {asset} to {currency}
// at the current price (minus fees)
else if(what === 'short') {
cost = (1 - this.fee) * (this.portfolio.asset * this.price);
amount = this.portfolio.asset;
this.portfolio.currency += this.extractFee(this.portfolio.asset * this.price);
this.portfolio.asset = 0;
cost = (1 - this.fee) * (this.portfolio.asset * this.price);
amount = this.portfolio.asset; //0 1.18*928=1095
this.portfolio.currency = this.extractFee(this.portfolio.asset * this.price);
this.portfolio.currency = this.portfolio.previouscurrency + (this.portfolio.previouscurrency - this.portfolio.currency);
this.portfolio.asset = 0;
this.exposed = false;
this.trades++;
this.exposed = false;
this.trades++;
}
else if(what === 'short') { //actually an opening short
if (this.portfolio.currency == 0) {
this.portfolio.currency = this.portfolio.asset * this.price;
this.portfolio.asset = 0;
}
cost = (1 - this.fee) * this.portfolio.currency;
this.portfolio.asset += this.extractFee(this.portfolio.currency / this.price);
amount = this.portfolio.asset;
this.portfolio.previouscurrency = this.extractFee(this.portfolio.currency);
this.portfolio.currency = 0;
this.exposed = true;
this.trades++;
}
}
const effectivePrice = this.price * this.fee;

+ 39
- 10
plugins/performanceAnalyzer/logger.js View File

@ -10,6 +10,10 @@ const mode = util.gekkoMode();
const log = require(dirs.core + 'log');
const colors = require('colors/safe');
const config = util.getConfig();
const perfConfig = config.performanceAnalyzer;
const shortTrading = perfConfig.shortTrading != undefined ? perfConfig.shortTrading : false;
const Logger = function(watchConfig) {
this.currency = watchConfig.currency;
this.asset = watchConfig.asset;
@ -64,26 +68,51 @@ if(mode === 'backtest') {
var at = trade.date.format('YYYY-MM-DD HH:mm:ss');
if(trade.action === 'sell') {
if (!shortTrading) {
if(trade.action === 'sell') {
let tradeType = trade.trigger.origin != undefined && trade.trigger.origin != 'advice' ? trade.trigger.origin : '';
let trailPercent = tradeType == 'trailingStop' ? ' ' + trade.trigger.trailPercentage + '%' : '';
let trailSLInfo = tradeType !== '' && trailPercent !== '' ? ` (${tradeType}${trailPercent})` : '';
log.info(
`${at}: Paper trader simulated a SELL${trailSLInfo} @ ${trade.price.toFixed(2)} ${this.currency}`,
`\t${this.round(trade.portfolio.currency)}`,
`${this.currency} <= ${this.round(trade.portfolio.asset)}`,
`${this.asset}`
);
}
else if(trade.action === 'buy') {
log.info(
`${at}: Paper trader simulated a BUY @ ${trade.price.toFixed(2)} ${this.currency}`,
`\t\t${this.round(trade.portfolio.currency)}`,
`${this.currency}\t=> ${this.round(trade.portfolio.asset)}`,
`${this.asset}`
);
}
}
if (shortTrading) {
if(trade.action === 'sell') {
let tradeType = trade.trigger.origin != undefined && trade.trigger.origin != 'advice' ? trade.trigger.origin : '';
let trailPercent = tradeType == 'trailingStop' ? ' ' + trade.trigger.trailPercentage + '%' : '';
let trailSLInfo = tradeType !== '' && trailPercent !== '' ? ` (${tradeType}${trailPercent})` : '';
log.info(
`${at}: Paper trader simulated a SELL${trailSLInfo} @ ${trade.price.toFixed(2)} ${this.currency}`,
`${at}: Paper trader simulated an OPEN SHORT position${trailSLInfo} @ ${trade.price.toFixed(2)} ${this.currency}`,
`\t${this.round(trade.portfolio.currency)}`,
`${this.currency} <= ${this.round(trade.portfolio.asset)}`,
`${this.asset}`
);
}
else if(trade.action === 'buy') {
log.info(
`${at}: Paper trader simulated a CLOSE SHORT position @ ${trade.price.toFixed(2)} ${this.currency}`,
`\t\t${this.round(trade.portfolio.currency)}`,
`${this.currency}\t=> ${this.round(trade.portfolio.asset)}`,
`${this.asset}`
);
}
}
else if(trade.action === 'buy')
log.info(
`${at}: Paper trader simulated a BUY @ ${trade.price.toFixed(2)} ${this.currency}`,
`\t\t${this.round(trade.portfolio.currency)}`,
`${this.currency}\t=> ${this.round(trade.portfolio.asset)}`,
`${this.asset}`
);
}
Logger.prototype.finalize = function(report) {

+ 76
- 2
plugins/performanceAnalyzer/performanceAnalyzer.js View File

@ -10,6 +10,7 @@ const ENV = util.gekkoEnv();
const config = util.getConfig();
const perfConfig = config.performanceAnalyzer;
const watchConfig = config.watch;
const shortTrading = perfConfig.shortTrading != undefined ? perfConfig.shortTrading : false;
const Logger = require('./logger');
@ -93,7 +94,13 @@ PerformanceAnalyzer.prototype.processCandle = function(candle, done) {
}
PerformanceAnalyzer.prototype.emitRoundtripUpdate = function() {
const uPnl = this.price - this.roundTrip.entry.price;
var uPnl;
if (shortTrading) {
uPnl = this.roundTrip.entry.price - this.price;
}
else {
uPnl = this.price - this.roundTrip.entry.price;
}
this.deferredEmit('roundtripUpdate', {
at: this.dates.end,
@ -108,7 +115,12 @@ PerformanceAnalyzer.prototype.processTradeCompleted = function(trade) {
this.portfolio = trade.portfolio;
this.balance = trade.balance;
this.registerRoundtripPart(trade);
if (shortTrading) {
this.registerShortRoundtripPart(trade);
}
else {
this.registerRoundtripPart(trade);
}
const report = this.calculateReportStatistics();
if(report) {
@ -147,6 +159,36 @@ PerformanceAnalyzer.prototype.registerRoundtripPart = function(trade) {
}
}
PerformanceAnalyzer.prototype.registerShortRoundtripPart = function(trade) {
if(this.trades === 1 && trade.action === 'buy') {
// this is not part of a valid roundtrip
return;
}
if(trade.action === 'buy') {
this.roundTrip.exit = {
date: trade.date,
price: trade.price,
total: trade.portfolio.currency + (trade.portfolio.asset * trade.price),
}
this.openRoundTrip = false;
this.handleCompletedShortRoundtrip();
} else if(trade.action === 'sell') {
if (this.roundTrip.exit) {
this.roundTrip.id++;
this.roundTrip.exit = false
}
this.roundTrip.entry = {
date: trade.date,
price: trade.price,
total: trade.portfolio.currency + (trade.portfolio.asset * trade.price),
}
this.openRoundTrip = true;
}
}
PerformanceAnalyzer.prototype.handleCompletedRoundtrip = function() {
var roundtrip = {
id: this.roundTrip.id,
@ -179,6 +221,38 @@ PerformanceAnalyzer.prototype.handleCompletedRoundtrip = function() {
}
PerformanceAnalyzer.prototype.handleCompletedShortRoundtrip = function() {
var roundtrip = {
id: this.roundTrip.id,
entryAt: this.roundTrip.entry.date,
entryPrice: this.roundTrip.entry.price,
entryBalance: this.roundTrip.entry.total,
exitAt: this.roundTrip.exit.date,
exitPrice: this.roundTrip.exit.price,
exitBalance: this.roundTrip.exit.total,
duration: this.roundTrip.exit.date.diff(this.roundTrip.entry.date)
}
roundtrip.pnl = roundtrip.exitBalance - roundtrip.entryBalance;
roundtrip.profit = (100 * roundtrip.exitBalance / roundtrip.entryBalance) - 100;
this.roundTrips[this.roundTrip.id] = roundtrip;
this.logger.handleRoundtrip(roundtrip);
this.deferredEmit('roundtrip', roundtrip);
// update cached exposure
this.exposure = this.exposure + Date.parse(this.roundTrip.exit.date) - Date.parse(this.roundTrip.entry.date);
// track losses separately for downside report
if (roundtrip.exitBalance > roundtrip.entryBalance)
this.losses.push(roundtrip);
}
PerformanceAnalyzer.prototype.calculateReportStatistics = function() {
if(!this.start.balance || !this.start.portfolio) {
log.error('Cannot calculate a profit report without having received portfolio data.');

+ 7
- 5
sample-eth.js View File

@ -65,11 +65,12 @@ config.T5mainasync = {
optInTimePeriod: 14
},
thresholds: {
RSIhigh: 66,
RSIlow: 44,
RSIhigh: 66, //66 //66
RSIlow: 44, //44 //41
MACDhigh: 0,
MACDlow: 0.88,
MACDlow: 1, //0.88
TEMAmin: 100.065,
TEMAmax: 100.065, //99.935,
persistance: 1
}
};
@ -100,6 +101,7 @@ config.paperTrader = {
}
config.performanceAnalyzer = {
shortTrading: true,
enabled: true,
riskFreeReturn: 5
}
@ -125,8 +127,8 @@ config.profitSimulator = {
enabled: true,
reportInCurrency: true,
simulationBalance: {
asset: 1,
currency: 0,
asset: 0,
currency: 1000,
},
verbose: true,
fee: 0.25,

Loading…
Cancel
Save