UNPKG

@backtest/framework

Version:

Backtesting trading strategies in TypeScript / JavaScript

133 lines 6.97 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.runStrategy = runStrategy; const prisma_strategies_1 = require("../../helpers/prisma-strategies"); const prisma_historical_data_1 = require("../../helpers/prisma-historical-data"); const run_strategy_1 = require("../../helpers/run-strategy"); const error_1 = require("../../helpers/error"); const parse_1 = require("../../helpers/parse"); async function runStrategy(options) { var _a, _b; if (!options) { throw new error_1.BacktestError('No options specified', error_1.ErrorCode.MissingInput); } if (!options.strategyName) { throw new error_1.BacktestError('Strategy name must be specified', error_1.ErrorCode.MissingInput); } if (!((_a = options.historicalData) === null || _a === void 0 ? void 0 : _a.length)) { throw new error_1.BacktestError('Historical data names must be specified', error_1.ErrorCode.MissingInput); } const data = { percentFee: 0, percentSlippage: 0, ...options }; data.startingAmount = data.startingAmount || 1000; const runParams = { strategyName: options.strategyName, historicalData: [], supportHistoricalData: options.supportHistoricalData || [], startingAmount: 0, startTime: 0, endTime: 0, params: {}, percentFee: 0, percentSlippage: 0, rootPath: options.rootPath }; const strategyMetaDatas = await (0, prisma_strategies_1.getAllStrategies)(); if (!(strategyMetaDatas === null || strategyMetaDatas === void 0 ? void 0 : strategyMetaDatas.length)) { throw new error_1.BacktestError('There are no saved strategies', error_1.ErrorCode.StrategyNotFound); } const strategyToRun = strategyMetaDatas.find((strategy) => strategy.name == options.strategyName); if (!strategyToRun) { throw new error_1.BacktestError(`Strategy ${options.strategyName} not found`, error_1.ErrorCode.StrategyNotFound); } let historicalDataSets = await (0, prisma_historical_data_1.getAllCandleMetaData)(); if (!(historicalDataSets === null || historicalDataSets === void 0 ? void 0 : historicalDataSets.length)) { throw new error_1.BacktestError('There are no saved historical data', error_1.ErrorCode.NotFound); } historicalDataSets = historicalDataSets.filter((data) => options.historicalData.includes(data.name)); if (historicalDataSets.length !== options.historicalData.length) { throw new error_1.BacktestError('Some historical data sets are missing or duplicated', error_1.ErrorCode.NotFound); } const names = historicalDataSets.map((data) => data.name); runParams.historicalData.push(...names); const isMultiSymbol = runParams.historicalData.length > 1; const firstHistoricalData = await (0, prisma_historical_data_1.getCandleMetaData)(runParams.historicalData[0]); if (!firstHistoricalData) { throw new error_1.BacktestError('Historical data not found', error_1.ErrorCode.NotFound); } const metaDataStrategy = await (0, prisma_strategies_1.getStrategy)(runParams.strategyName); if (!metaDataStrategy) { throw new error_1.BacktestError('Strategy not found', error_1.ErrorCode.StrategyNotFound); } let paramsCache = {}; for (const param of Object.keys(data.params)) { if (!metaDataStrategy.params.find((p) => param == p)) { throw new error_1.BacktestError(`Input param ${param} does not exist in the strategy's properties`, error_1.ErrorCode.InvalidInput); } let value = data.params[param]; if (value === undefined || value === '') value = 0; paramsCache[param] = isNaN(+value) ? value : +value; } runParams.params = paramsCache; runParams.startTime = new Date(data.startTime || firstHistoricalData.startTime).getTime(); runParams.endTime = new Date(data.endTime || firstHistoricalData.endTime).getTime(); for (const data of historicalDataSets) { if (runParams.startTime < data.startTime || runParams.startTime > data.endTime) { throw new error_1.BacktestError(`Start date must be between ${(0, parse_1.dateToString)(data.startTime)} and ${(0, parse_1.dateToString)(data.endTime)}`, error_1.ErrorCode.InvalidInput); } if (runParams.endTime > data.endTime || runParams.endTime <= runParams.startTime) { throw new error_1.BacktestError(`End date must be between ${(0, parse_1.dateToString)(runParams.startTime)} and ${(0, parse_1.dateToString)(data.endTime)}`, error_1.ErrorCode.InvalidInput); } } runParams.startingAmount = +data.startingAmount; runParams.percentFee = +data.percentFee; runParams.percentSlippage = +data.percentSlippage; const strageyResults = await (0, run_strategy_1.run)(runParams); if (!strageyResults) { throw new error_1.BacktestError('Strategy results not found', error_1.ErrorCode.NotFound); } await (0, prisma_strategies_1.updateLastRunTime)(runParams.strategyName, new Date().getTime()); const isRunStrategyResult = !Array.isArray(strageyResults) && typeof (strageyResults === null || strageyResults === void 0 ? void 0 : strageyResults.runMetaData) === 'object'; if (!isRunStrategyResult || isMultiSymbol) { const permutations = strageyResults; return { name: `${runParams.strategyName}-${firstHistoricalData.symbol}-multi`, strategyName: runParams.strategyName, symbols: runParams.historicalData, permutationCount: permutations.length, params: paramsCache, startTime: runParams.startTime, endTime: runParams.endTime, txFee: runParams.percentFee, slippage: runParams.percentSlippage, startingAmount: runParams.startingAmount, multiResults: permutations, isMultiValue: permutations !== undefined, isMultiSymbol: isMultiSymbol }; } if (!((_b = strageyResults.allOrders) === null || _b === void 0 ? void 0 : _b.length)) { throw new error_1.BacktestError('Strategy did not perform any trades over the given time period', error_1.ErrorCode.TradeNotProcessed); } return { name: `${runParams.strategyName}-${firstHistoricalData.name}`, historicalDataName: firstHistoricalData.name, candleMetaData: firstHistoricalData, candles: strageyResults.allCandles, strategyName: runParams.strategyName, params: runParams.params, startTime: runParams.startTime, endTime: runParams.endTime, startingAmount: runParams.startingAmount, txFee: runParams.percentFee, slippage: runParams.percentSlippage, runMetaData: strageyResults.runMetaData, allOrders: strageyResults.allOrders, allWorths: strageyResults.allWorths }; } //# sourceMappingURL=run.js.map