first commit
This commit is contained in:
commit
aa7869eb33
386 files changed
+69375
No files matched your search
@@ -0,0 +1,283 @@
|
||||
const _ = require('lodash');
|
||||
|
||||
const util = require('../../core/util');
|
||||
const ENV = util.gekkoEnv();
|
||||
|
||||
const config = util.getConfig();
|
||||
const calcConfig = config.paperTrader;
|
||||
const watchConfig = config.watch;
|
||||
const dirs = util.dirs();
|
||||
const log = require(dirs.core + 'log');
|
||||
|
||||
const TrailingStop = require(dirs.broker + 'triggers/trailingStop');
|
||||
|
||||
const PaperTrader = function() {
|
||||
_.bindAll(this);
|
||||
|
||||
if(calcConfig.feeUsing === 'maker') {
|
||||
this.rawFee = calcConfig.feeMaker;
|
||||
} else {
|
||||
this.rawFee = calcConfig.feeTaker;
|
||||
}
|
||||
|
||||
this.fee = 1 - this.rawFee / 100;
|
||||
|
||||
this.currency = watchConfig.currency;
|
||||
this.asset = watchConfig.asset;
|
||||
|
||||
this.portfolio = {
|
||||
asset: calcConfig.simulationBalance.asset,
|
||||
currency: calcConfig.simulationBalance.currency,
|
||||
}
|
||||
|
||||
this.balance = false;
|
||||
|
||||
if(this.portfolio.asset > 0) {
|
||||
this.exposed = true;
|
||||
}
|
||||
|
||||
this.propogatedTrades = 0;
|
||||
this.propogatedTriggers = 0;
|
||||
|
||||
this.warmupCompleted = false;
|
||||
|
||||
this.warmupCandle;
|
||||
}
|
||||
|
||||
PaperTrader.prototype.relayPortfolioChange = function() {
|
||||
this.deferredEmit('portfolioChange', {
|
||||
asset: this.portfolio.asset,
|
||||
currency: this.portfolio.currency
|
||||
});
|
||||
}
|
||||
|
||||
PaperTrader.prototype.relayPortfolioValueChange = function() {
|
||||
this.deferredEmit('portfolioValueChange', {
|
||||
balance: this.getBalance()
|
||||
});
|
||||
}
|
||||
|
||||
PaperTrader.prototype.extractFee = function(amount) {
|
||||
amount *= 1e8;
|
||||
amount *= this.fee;
|
||||
amount = Math.floor(amount);
|
||||
amount /= 1e8;
|
||||
return amount;
|
||||
}
|
||||
|
||||
PaperTrader.prototype.setStartBalance = function() {
|
||||
this.balance = this.getBalance();
|
||||
}
|
||||
|
||||
// after every succesfull trend ride we hopefully end up
|
||||
// with more BTC than we started with, this function
|
||||
// calculates Gekko's profit in %.
|
||||
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;
|
||||
|
||||
this.exposed = true;
|
||||
this.trades++;
|
||||
}
|
||||
|
||||
// virtually trade all {currency} to {asset}
|
||||
// at the current price (minus fees)
|
||||
else if(what === 'short') {
|
||||
cost = (1 - this.fee) * (this.portfolio.asset * this.price);
|
||||
this.portfolio.currency += this.extractFee(this.portfolio.asset * this.price);
|
||||
amount = this.portfolio.currency / this.price;
|
||||
this.portfolio.asset = 0;
|
||||
|
||||
this.exposed = false;
|
||||
this.trades++;
|
||||
}
|
||||
|
||||
const effectivePrice = this.price * this.fee;
|
||||
|
||||
return { cost, amount, effectivePrice };
|
||||
}
|
||||
|
||||
PaperTrader.prototype.getBalance = function() {
|
||||
return this.portfolio.currency + this.price * this.portfolio.asset;
|
||||
}
|
||||
|
||||
PaperTrader.prototype.now = function() {
|
||||
return this.candle.start.clone().add(1, 'minute');
|
||||
}
|
||||
|
||||
PaperTrader.prototype.processAdvice = function(advice) {
|
||||
let action;
|
||||
if(advice.recommendation === 'short') {
|
||||
action = 'sell';
|
||||
|
||||
// clean up potential old stop trigger
|
||||
if(this.activeStopTrigger) {
|
||||
this.deferredEmit('triggerAborted', {
|
||||
id: this.activeStopTrigger.id,
|
||||
date: advice.date
|
||||
});
|
||||
|
||||
delete this.activeStopTrigger;
|
||||
}
|
||||
|
||||
} else if(advice.recommendation === 'long') {
|
||||
action = 'buy';
|
||||
|
||||
if(advice.trigger) {
|
||||
|
||||
// clean up potential old stop trigger
|
||||
if(this.activeStopTrigger) {
|
||||
this.deferredEmit('triggerAborted', {
|
||||
id: this.activeStopTrigger.id,
|
||||
date: advice.date
|
||||
});
|
||||
|
||||
delete this.activeStopTrigger;
|
||||
}
|
||||
|
||||
this.createTrigger(advice);
|
||||
}
|
||||
} else {
|
||||
return log.warn(
|
||||
`[Papertrader] ignoring unknown advice recommendation: ${advice.recommendation}`
|
||||
);
|
||||
}
|
||||
|
||||
this.tradeId = 'trade-' + (++this.propogatedTrades);
|
||||
|
||||
this.deferredEmit('tradeInitiated', {
|
||||
id: this.tradeId,
|
||||
adviceId: advice.id,
|
||||
action,
|
||||
portfolio: _.clone(this.portfolio),
|
||||
balance: this.getBalance(),
|
||||
date: advice.date,
|
||||
});
|
||||
|
||||
const { cost, amount, effectivePrice } = this.updatePosition(advice.recommendation);
|
||||
|
||||
this.relayPortfolioChange();
|
||||
this.relayPortfolioValueChange();
|
||||
|
||||
this.deferredEmit('tradeCompleted', {
|
||||
id: this.tradeId,
|
||||
adviceId: advice.id,
|
||||
action,
|
||||
cost,
|
||||
amount,
|
||||
price: this.price,
|
||||
portfolio: this.portfolio,
|
||||
balance: this.getBalance(),
|
||||
date: advice.date,
|
||||
effectivePrice,
|
||||
feePercent: this.rawFee
|
||||
});
|
||||
}
|
||||
|
||||
PaperTrader.prototype.createTrigger = function(advice) {
|
||||
const trigger = advice.trigger;
|
||||
|
||||
if(trigger && trigger.type === 'trailingStop') {
|
||||
|
||||
if(!trigger.trailValue) {
|
||||
return log.warn(`[Papertrader] ignoring trailing stop without trail value`);
|
||||
}
|
||||
|
||||
const triggerId = 'trigger-' + (++this.propogatedTriggers);
|
||||
|
||||
this.deferredEmit('triggerCreated', {
|
||||
id: triggerId,
|
||||
at: advice.date,
|
||||
type: 'trailingStop',
|
||||
proprties: {
|
||||
trail: trigger.trailValue,
|
||||
initialPrice: this.price,
|
||||
}
|
||||
});
|
||||
|
||||
this.activeStopTrigger = {
|
||||
id: triggerId,
|
||||
adviceId: advice.id,
|
||||
instance: new TrailingStop({
|
||||
initialPrice: this.price,
|
||||
trail: trigger.trailValue,
|
||||
onTrigger: this.onStopTrigger
|
||||
})
|
||||
}
|
||||
} else {
|
||||
log.warn(`[Papertrader] Gekko does not know trigger with type "${trigger.type}".. Ignoring stop.`);
|
||||
}
|
||||
}
|
||||
|
||||
PaperTrader.prototype.onStopTrigger = function() {
|
||||
|
||||
const date = this.now();
|
||||
|
||||
this.deferredEmit('triggerFired', {
|
||||
id: this.activeStopTrigger.id,
|
||||
date
|
||||
});
|
||||
|
||||
const { cost, amount, effectivePrice } = this.updatePosition('short');
|
||||
|
||||
this.relayPortfolioChange();
|
||||
this.relayPortfolioValueChange();
|
||||
|
||||
this.deferredEmit('tradeCompleted', {
|
||||
id: this.tradeId,
|
||||
adviceId: this.activeStopTrigger.adviceId,
|
||||
action: 'sell',
|
||||
cost,
|
||||
amount,
|
||||
price: this.price,
|
||||
portfolio: this.portfolio,
|
||||
balance: this.getBalance(),
|
||||
date,
|
||||
effectivePrice,
|
||||
feePercent: this.rawFee
|
||||
});
|
||||
|
||||
delete this.activeStopTrigger;
|
||||
}
|
||||
|
||||
PaperTrader.prototype.processStratWarmupCompleted = function() {
|
||||
this.warmupCompleted = true;
|
||||
this.processCandle(this.warmupCandle, _.noop);
|
||||
}
|
||||
|
||||
PaperTrader.prototype.processCandle = function(candle, done) {
|
||||
if(!this.warmupCompleted) {
|
||||
this.warmupCandle = candle;
|
||||
return done();
|
||||
}
|
||||
|
||||
this.price = candle.close;
|
||||
this.candle = candle;
|
||||
|
||||
if(!this.balance) {
|
||||
this.setStartBalance();
|
||||
this.relayPortfolioChange();
|
||||
this.relayPortfolioValueChange();
|
||||
}
|
||||
|
||||
if(this.exposed) {
|
||||
this.relayPortfolioValueChange();
|
||||
}
|
||||
|
||||
if(this.activeStopTrigger) {
|
||||
this.activeStopTrigger.instance.updatePrice(this.price);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
|
||||
module.exports = PaperTrader;
|
||||
Reference in new issue
Block a user