crypto-backtest
Version:
Backtesting on market data imported from crypto exchange
87 lines • 4.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Advisor = void 0;
const IndicatorService_1 = require("./IndicatorService");
const StrategyExecuteData_1 = require("./StrategyExecuteData");
class Advisor {
static async execute(candles, strategy) {
const { indicatorInputs, warmup: strategyWarmup, execute } = strategy;
const indicatorKeys = indicatorInputs.map((e) => e.key);
// советник заработает только когда наберется необходимое количество вычисленных индикаторов для стратегии
const warmup = strategyWarmup +
indicatorInputs
.map((e) => IndicatorService_1.IndicatorService.getStart(e.name, e.options))
.reduce((a, e) => Math.max(a, e));
return Promise.all(indicatorInputs.map((e) => IndicatorService_1.IndicatorService.execute({
candles,
name: e.name,
options: e.options,
}))).then((results) => {
// для каждой свечи попробовать найти вычисленный индикатор
const data = candles.map((candle) => {
const { time } = candle;
const indicators = indicatorKeys.map((key, index) => {
const indicator = results[index].find((e) => e.time === time);
return {
key,
outputs: indicator ? indicator.values : [],
};
});
const dataItem = new StrategyExecuteData_1.StrategyExecuteData({
time: candle.time,
candle,
indicators,
});
return dataItem;
});
const advices = [];
for (let i = warmup; i < data.length; i++) {
const side = execute(data.slice(i - warmup, i + 1));
advices.push({
time: data[i].time,
side,
});
}
return Promise.resolve(advices);
});
}
static idealExecute(candles, fee) {
const advices = [];
const lastIndex = candles.length - 1;
let min = candles[0];
let max = null;
// max по времени всегда позднее open
// max появляется только если current первысил порог fee
// если current упал ниже max больше чем на fee, то пара фиксируется и начинается заново
// порог = min * fee + max * fee + min
candles.forEach((curr, i) => {
if (curr.close > (min.close * (1 + fee)) / (1 - fee) &&
(!max || curr.close > max.close)) {
// если рост и преодолен порог, то фиксируется новый пик
max = curr;
}
if (max &&
(max.close > (curr.close * (1 + fee)) / (1 - fee) ||
i === lastIndex)) {
// если падение ниже порога и после пика, то фиксируется пара
advices.push({
time: min.time,
side: "buy",
});
advices.push({
time: max.time,
side: "sell",
});
min = curr; // считается новой впадиной
max = null; // пик сбрасывается
}
if (!max && curr.close < min.close) {
// если пика нет, и понижение, то фиксируется новая отметка впадина
min = curr;
}
});
return advices;
}
}
exports.Advisor = Advisor;
//# sourceMappingURL=Advisor.js.map