quant-zero
Version:
Node-Quant is a powerful Node.js package for developing and testing quantitative trading strategies in cryptocurrency markets, offering tools for backtesting and performance analysis.
1 lines • 60.3 kB
Source Map (JSON)
{"version":3,"sources":["../src/index.ts","../src/lib/ChartingSystem.ts","../src/lib/Indicator.ts","../src/types.ts","../src/lib/Logger.ts","../src/lib/Strategy.ts","../src/utils/dataInstaller.ts","../src/exchange.ts","../src/utils/parseOHLCV.ts","../src/charting/CandleSticks.ts","../src/charting/HeikenAishi.ts","../src/charting/Renko.ts","../src/utils/generateId.ts","../src/utils/maths/pl.ts","../src/lib/Trade.ts","../src/managers/TradeManager.ts","../src/utils/maths/alpha.ts","../src/utils/maths/beta.ts","../src/utils/maths/covariance.ts","../src/utils/maths/sharpeE.ts","../src/utils/maths/variance.ts","../src/utils/maths/standarddev.ts","../src/utils/getAvgMarketReturn.ts","../src/managers/Report/ReportManager.ts","../src/indicators/index.ts","../src/indicators/EMA.ts","../src/indicators/SMA.ts","../src/indicators/ATR.ts","../src/indicators/RSI.ts","../src/indicators/MACD.ts","../src/indicators/StochRSI.ts","../src/indicators/BB.ts","../src/indicators/IchimokuCloud.ts","../src/indicators/WEMA.ts","../src/indicators/ADX.ts","../src/indicators/VolumeProfile.ts","../src/indicators/VWAP.ts","../src/indicators/ForceIndex.ts"],"sourcesContent":["export * from '@/lib/';\r\nexport * from '@/managers'\r\nexport * as Indicators from '@/indicators'\r\nexport * from '@/types'\r\n","import ta from 'technicalindicators'\r\n\r\nexport class ChartingSystem {\r\n public transform(rawData: any): ta.CandleList {\r\n return rawData\r\n }\r\n}","import { EventEmitter } from 'events'\r\nimport {\r\n TimelineEventsInterface,\r\n IndicatorOptions,\r\n TimelineEvents,\r\n} from '@/types'\r\nimport { OHLCV } from 'ccxt'\r\n\r\nexport class Indicator extends EventEmitter {\r\n protected data: OHLCV[] = []\r\n public name: string\r\n public key: string\r\n public description: string\r\n\r\n constructor({ name, key, description }: IndicatorOptions) {\r\n super()\r\n this.name = name\r\n this.key = key\r\n this.description = description\r\n }\r\n\r\n // Provides the first sample of data if not already provided\r\n public provide(data: OHLCV[]) {\r\n if (this.data.length === 0 && data.length > 0) {\r\n this.data = data\r\n this.emit(TimelineEvents.PROVIDED, this.data)\r\n }\r\n }\r\n\r\n // Feeds new data in\r\n public feed(data: OHLCV) {\r\n this.data.push(data)\r\n this.emit(TimelineEvents.FED, data)\r\n }\r\n\r\n // Generates the result\r\n public generate(): any {\r\n return null\r\n }\r\n\r\n public clear(): void {\r\n this.data = [];\r\n }\r\n\r\n on<K extends keyof TimelineEventsInterface>(\r\n event: K,\r\n listener: TimelineEventsInterface[K],\r\n ): this {\r\n return super.on(event, listener)\r\n }\r\n}\r\n","import { Num, OHLCV } from 'ccxt'\r\nimport { ChartingSystem, Indicator, Trade } from '@/lib'\r\nimport { EventEmitter } from 'events'\r\n\r\nexport interface BacktestResults {\r\n alpha: number\r\n beta: number\r\n sharpeE: number\r\n maxDrawdown: number\r\n maxProfit: number\r\n profitFactor: number\r\n return: number\r\n percentageProfitable: number\r\n tradeCount: number\r\n reportData: {\r\n trades: Trade[]\r\n data: DataSource[]\r\n }\r\n}\r\n\r\nexport interface CandleStickObject {\r\n open?: Num\r\n high?: Num\r\n low?: Num\r\n close?: Num\r\n volume?: Num\r\n timestamp?: Num\r\n}\r\n\r\nexport interface CreateTradeOptions {\r\n positionType: PositionType\r\n orderType: OrderType\r\n size: number\r\n limitPrice?: number\r\n leverage?: number\r\n open?: boolean\r\n riskOptions?: {\r\n TP?: number\r\n SL?: number\r\n }\r\n}\r\n\r\nexport enum CryptoPair {\r\n BTCUSDT = 'BTC/USDT',\r\n ETHUSDT = 'ETH/USDT',\r\n SOLUSDT = 'SOL/USDT',\r\n BNBUSDT = 'BNB/USDT',\r\n LTCUSDT = 'LTC/USDT',\r\n DOGEUSDT = 'DOGE/USDT',\r\n}\r\n\r\nexport interface IndicatorOptions {\r\n name: string\r\n description: string\r\n key: string\r\n indicators?: Indicator[]\r\n}\r\n\r\nexport interface SimulationOptions {\r\n capital?: number\r\n leverage?: number\r\n fees?: number\r\n pair: CryptoPair\r\n}\r\n\r\nexport interface StrategyOptions {\r\n name: string\r\n pair: CryptoPair\r\n dataLength: number\r\n timeFrame: TimeFrame\r\n indicators?: Indicator[]\r\n chartType?: ChartingSystem\r\n simulationOptions?: SimulationOptions\r\n}\r\n\r\nexport interface TimelineManagerSystem extends EventEmitter {\r\n key: string\r\n generate: () => any\r\n provide: (data: OHLCV[]) => void\r\n feed: (data: OHLCV) => void\r\n}\r\n\r\nexport interface TimelineProfile {\r\n key: string\r\n data: any[]\r\n}\r\n\r\nexport type TimelineEventsInterface = {\r\n provided: (data: any[]) => any[]\r\n fed: (data: any) => any\r\n generated: () => any[]\r\n}\r\n\r\nexport enum TimelineEvents {\r\n PROVIDED = 'provided',\r\n FED = 'fed',\r\n GENERATED = 'generated',\r\n}\r\n\r\nexport enum NumberReturnType {\r\n RELATIVE = 'relative',\r\n FIXED = 'fixed',\r\n}\r\n\r\nexport enum OrderType {\r\n MARKET = 'market',\r\n LIMIT = 'limit',\r\n STOP = 'stop',\r\n}\r\n\r\nexport enum PositionType {\r\n LONG = 'long',\r\n SHORT = 'short',\r\n}\r\n\r\nexport enum TRADE_KEY {\r\n ID = 'id',\r\n isOpen = 'isOpen',\r\n isClosed = 'isClosed',\r\n PL = 'PL',\r\n TP = 'TP',\r\n SL = 'SL',\r\n positionSize = 'size',\r\n leverage = 'leverage',\r\n drawdown = 'drawdown',\r\n openPrice = 'openPrice',\r\n closePrice = 'closePrice',\r\n fee = 'fee',\r\n pair = 'pair',\r\n blockChainTrack = 'blockChainTrack',\r\n orderType = 'orderType',\r\n positionType = 'positionType',\r\n timestamp = 'timestamp',\r\n}\r\n\r\nexport interface TradeData {\r\n [TRADE_KEY.ID]: string\r\n [TRADE_KEY.isOpen]: boolean\r\n [TRADE_KEY.isClosed]: boolean\r\n [TRADE_KEY.PL]?: number\r\n [TRADE_KEY.TP]?: number\r\n [TRADE_KEY.SL]?: number\r\n [TRADE_KEY.positionSize]: number\r\n [TRADE_KEY.leverage]: number\r\n [TRADE_KEY.drawdown]: number\r\n [TRADE_KEY.openPrice]: undefined\r\n [TRADE_KEY.closePrice]: undefined\r\n [TRADE_KEY.fee]?: number\r\n [TRADE_KEY.blockChainTrack]: number\r\n [TRADE_KEY.pair]: CryptoPair\r\n [TRADE_KEY.orderType]: OrderType\r\n [TRADE_KEY.positionType]: PositionType\r\n [TRADE_KEY.timestamp]: number\r\n}\r\n\r\nexport interface TradeOptions {\r\n open?: boolean\r\n positionSize: number\r\n leverage?: number\r\n TP?: number\r\n SL?: number\r\n pair: CryptoPair\r\n orderType: OrderType\r\n positionType: PositionType\r\n isLive?: boolean\r\n}\r\n\r\nexport interface DataSource {\r\n name: string;\r\n type: DataSourceType\r\n data: any[]\r\n}\r\n\r\nexport enum DataSourceType {\r\n PRICE = 'price',\r\n FACTOR = 'factor',\r\n}\r\n\r\nexport enum TimeFrame {\r\n SECOND = '1s',\r\n MINUTE = '1m',\r\n TWO_MINUTES = '2m',\r\n THREE_MINUTES = '3m',\r\n FIVE_MINUTES = '5m',\r\n TEN_MINUTES = '10m',\r\n FIFTEEN_MINUTES = '15m',\r\n THIRTY_MINUTES = '30m',\r\n FORTY_FIVE_MINUTES = '45m',\r\n HOUR = '1h',\r\n TWO_HOURS = '2h',\r\n THREE_HOURS = '3h',\r\n FOUR_HOURS = '4h',\r\n DAY = '1d',\r\n WEEK = '1w',\r\n MONTH = '1M',\r\n}\r\n","import chalk from 'chalk'\r\n\r\nexport class Logger {\r\n public success(payload: string) {\r\n console.log(chalk.green(payload))\r\n }\r\n public debug(payload: any) {\r\n console.log(payload)\r\n }\r\n public info(payload: string) {\r\n console.log(chalk.cyan(payload))\r\n }\r\n public warn(payload: string) {\r\n console.log(chalk.yellow(payload))\r\n }\r\n public error(payload: string) {\r\n console.log(chalk.red(payload))\r\n }\r\n}\r\n\r\nexport const logger = new Logger();","import fs from 'fs'\r\nimport path from 'path'\r\nimport {\r\n BacktestResults,\r\n DataSourceType,\r\n SimulationOptions,\r\n StrategyOptions,\r\n TimeFrame,\r\n TRADE_KEY,\r\n} from '@/types'\r\nimport { OHLCV } from 'ccxt'\r\nimport downloadPairData from '@/utils/dataInstaller'\r\nimport { CandleSticks } from '@/charting'\r\nimport { TradeManager } from '@/managers/TradeManager'\r\nimport {\r\n calcAlpha,\r\n calcBeta,\r\n calcCovariance,\r\n calcPL,\r\n calcSharpeE,\r\n calcStddev,\r\n calcVariance,\r\n} from '@/utils/maths'\r\nimport getAvgMarketReturn from '@/utils/getAvgMarketReturn'\r\n\r\nexport class Strategy {\r\n public name: string;\r\n private data: OHLCV[] = []\r\n public readonly strategyOptions: Required<StrategyOptions>\r\n public tradeManager: TradeManager\r\n public indicators: Map<string, any> = new Map()\r\n public pairDataPath: string = ''\r\n\r\n constructor(strategyOptions: StrategyOptions) {\r\n const {\r\n name,\r\n dataLength = 100,\r\n timeFrame = TimeFrame.MINUTE,\r\n chartType = new CandleSticks(),\r\n indicators = [],\r\n simulationOptions,\r\n pair,\r\n } = strategyOptions\r\n\r\n this.name = strategyOptions.name;\r\n \r\n this.strategyOptions = {\r\n name,\r\n dataLength,\r\n timeFrame,\r\n chartType,\r\n indicators,\r\n simulationOptions: simulationOptions ?? { pair },\r\n pair,\r\n }\r\n\r\n this.tradeManager = new TradeManager(this)\r\n }\r\n\r\n public async loadData(): Promise<void> {\r\n const { pair, timeFrame, dataLength } = this.strategyOptions\r\n const dataFolderPath = path.join(process.cwd(), 'data')\r\n\r\n if (!fs.existsSync(dataFolderPath)) {\r\n fs.mkdirSync(dataFolderPath)\r\n }\r\n\r\n const { path: pairDataPath, data } = await downloadPairData(\r\n pair,\r\n timeFrame,\r\n dataLength,\r\n dataFolderPath,\r\n )\r\n\r\n this.pairDataPath = pairDataPath\r\n this.data = data\r\n }\r\n\r\n private provideAllIndicators(): void {\r\n this.strategyOptions.indicators.forEach((indicator) =>\r\n indicator.provide(this.data),\r\n )\r\n }\r\n\r\n private feedAllIndicators(data: OHLCV): void {\r\n this.strategyOptions.indicators.forEach((indicator) => indicator.feed(data))\r\n }\r\n\r\n public async backtest({}: SimulationOptions): Promise<BacktestResults> {\r\n const results: BacktestResults = {\r\n alpha: 0,\r\n beta: 0,\r\n maxDrawdown: 0,\r\n maxProfit: 0,\r\n percentageProfitable: 0,\r\n return: 0,\r\n profitFactor: 0,\r\n sharpeE: 0,\r\n tradeCount: 0,\r\n reportData: {\r\n data: [],\r\n trades: [],\r\n },\r\n }\r\n\r\n await Promise.all([this.internalStart(), this.onStart(this.data)])\r\n\r\n for (const update of this.data) {\r\n await Promise.all([\r\n this.internalUpdate(update, this.data),\r\n this.onUpdate(update, this.data),\r\n ])\r\n }\r\n\r\n const tradeHistory = this.tradeManager.getTradeHistory()\r\n results.tradeCount = tradeHistory.length\r\n\r\n results.reportData.trades = tradeHistory\r\n\r\n if (tradeHistory.length === 0) {\r\n return results\r\n }\r\n\r\n const returns = tradeHistory.map((trade) => {\r\n const { [TRADE_KEY.PL]: pl = 0, [TRADE_KEY.leverage]: leverage = 1 } =\r\n trade.getData()\r\n return pl * leverage\r\n })\r\n\r\n const totalPL = returns.reduce((sum, r) => sum + r, 0)\r\n const profitableTrades = returns.filter((r) => r > 0)\r\n const lossTrades = returns.filter((r) => r < 0)\r\n\r\n results.return = totalPL\r\n results.percentageProfitable =\r\n (profitableTrades.length / returns.length) * 100\r\n results.maxDrawdown = Math.min(...returns)\r\n results.maxProfit = Math.max(...returns)\r\n\r\n const grossProfit = profitableTrades.reduce((sum, r) => sum + r, 0)\r\n const grossLoss = Math.abs(lossTrades.reduce((sum, r) => sum + r, 0))\r\n results.profitFactor =\r\n grossLoss !== 0 ? grossProfit / grossLoss : grossProfit\r\n\r\n const averageReturn = totalPL / returns.length\r\n const riskFreeRate = 0\r\n const marketReturn = await getAvgMarketReturn(\r\n this.strategyOptions.pair,\r\n TimeFrame.MONTH,\r\n 60, // 5 years * 12 months\r\n )\r\n\r\n const covariance = calcCovariance(returns, marketReturn) || 0\r\n const variance = calcVariance(returns) || 0\r\n const standardDeviation = calcStddev(returns)\r\n\r\n results.beta = calcBeta(covariance, variance)\r\n results.alpha = calcAlpha(\r\n averageReturn,\r\n riskFreeRate,\r\n results.beta,\r\n marketReturn,\r\n )\r\n results.sharpeE = calcSharpeE(\r\n averageReturn,\r\n results.return,\r\n riskFreeRate,\r\n standardDeviation,\r\n )\r\n\r\n results.reportData.data.push({\r\n name: `${this.strategyOptions.pair.replaceAll('/', '_')}_${\r\n this.strategyOptions.timeFrame\r\n }`,\r\n type: DataSourceType.PRICE,\r\n data: this.data,\r\n })\r\n\r\n this.indicators.forEach((indicatorData, indicatorKey) => {\r\n results.reportData.data.push({\r\n name: indicatorKey,\r\n type: DataSourceType.FACTOR,\r\n data: indicatorData,\r\n })\r\n })\r\n\r\n return results\r\n }\r\n\r\n private internalStart() {\r\n // this.provideAllIndicators();\r\n this.strategyOptions.indicators.forEach((indicator) => {\r\n indicator.clear()\r\n })\r\n }\r\n\r\n private async internalUpdate(update: OHLCV, updates: OHLCV[]): Promise<void> {\r\n this.feedAllIndicators(update)\r\n\r\n await Promise.all(\r\n this.strategyOptions.indicators.map(async (indicator) => {\r\n this.indicators.set(indicator.key, await indicator.generate())\r\n }),\r\n )\r\n\r\n this.tradeManager.onUpdate(update, updates)\r\n }\r\n\r\n protected live() {}\r\n\r\n protected onStart(updates: OHLCV[]): void {}\r\n\r\n protected onUpdate(update: OHLCV, updates: OHLCV[]): void {}\r\n}\r\n","import fs from 'fs'\r\nimport path from 'path'\r\nimport { CryptoPair, TimeFrame } from '@/types'\r\nimport exchange from '@/exchange'\r\n\r\nexport default async function downloadPairData(\r\n pair: CryptoPair,\r\n timeFrame: TimeFrame,\r\n dataLength: number,\r\n dataFolderPath: string,\r\n) {\r\n const fileName = `${pair.replace('/', '_')}_${dataLength}_${timeFrame}.json`\r\n const filePath = path.join(dataFolderPath, fileName)\r\n\r\n if (fs.existsSync(filePath)) {\r\n console.log(\r\n `Data for ${pair} with length ${dataLength} already exists. Returning existing data.`,\r\n )\r\n const existingData = fs.readFileSync(filePath, 'utf-8')\r\n return { path: filePath, data: JSON.parse(existingData) }\r\n }\r\n\r\n console.log(`Installing ${dataLength} candles for ${pair}.`)\r\n const fetchedData = await exchange.fetchOHLCV(\r\n pair,\r\n timeFrame,\r\n undefined,\r\n dataLength,\r\n {\r\n paginate: true,\r\n },\r\n )\r\n\r\n fs.writeFileSync(filePath, JSON.stringify(fetchedData, null, 2))\r\n return { path: filePath, data: fetchedData }\r\n}\r\n","import ccxt from \"ccxt\";\r\n\r\nconst exchange = new ccxt.binance();\r\n\r\nexport default exchange;","import { Num, OHLCV } from \"ccxt\";\r\nimport { CandleStickObject } from \"@/types\";\r\n\r\nexport const parseIntoObject = (rawData: OHLCV[]) => {\r\n const candlesticks: CandleStickObject[] = [];\r\n\r\n for (const cell of rawData) {\r\n const [timestamp, open, high, low, close, volume] = cell;\r\n\r\n candlesticks.push({ timestamp, open, high, low, close, volume });\r\n }\r\n\r\n return candlesticks;\r\n};\r\n\r\nexport const parseIntoArrays = () => {};\r\n\r\nexport const parseIntoRows = (data: OHLCV[]) => {\r\n const timestamps: number[] = [];\r\n const opens: number[] = [];\r\n const highs: number[] = [];\r\n const lows: number[] = [];\r\n const closes: number[] = [];\r\n const volumes: number[] = [];\r\n\r\n data.forEach((candle) => {\r\n timestamps.push(candle[0]!);\r\n opens.push(candle[1]!);\r\n highs.push(candle[2]!);\r\n lows.push(candle[3]!);\r\n closes.push(candle[4]!);\r\n volumes.push(candle[5]!);\r\n });\r\n\r\n return { timestamps, opens, highs, lows, closes, volumes };\r\n};\r\n","import { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport { OHLCV } from 'ccxt'\r\nimport { ChartingSystem } from '@/lib'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class CandleSticks extends ChartingSystem {\r\n public transform(rawData: OHLCV[]): ta.CandleList {\r\n const { opens, highs, lows, closes, timestamps, volumes } =\r\n parseIntoRows(rawData)\r\n return {\r\n open: opens,\r\n high: highs,\r\n low: lows,\r\n close: closes,\r\n timestamp: timestamps,\r\n volume: volumes,\r\n }\r\n }\r\n}\r\n","import { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport { OHLCV } from 'ccxt'\r\nimport { ChartingSystem } from '@/lib'\r\nimport { HeikinAshiInput } from 'technicalindicators/declarations/chart_types/HeikinAshi'\r\nimport ta from 'technicalindicators'\r\n\r\ntype HeikenAishiOptions = Omit<\r\n HeikinAshiInput,\r\n 'low' | 'close' | 'high' | 'open'\r\n>\r\n\r\nexport class HeikenAishi extends ChartingSystem {\r\n public options: HeikenAishiOptions\r\n\r\n constructor(options: HeikenAishiOptions) {\r\n super()\r\n this.options = options\r\n }\r\n\r\n public transform(rawData: OHLCV[]) {\r\n const { opens, highs, lows, closes, volumes, timestamps } =\r\n parseIntoRows(rawData)\r\n return ta.heikinashi({\r\n ...this.options,\r\n open: opens,\r\n high: highs,\r\n low: lows,\r\n close: closes,\r\n volume: volumes,\r\n timestamp: timestamps,\r\n })\r\n }\r\n}\r\n","import { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport { OHLCV } from 'ccxt'\r\nimport { ChartingSystem } from '@/lib'\r\nimport ta from 'technicalindicators'\r\nimport { RenkoInput } from 'technicalindicators/declarations/chart_types/Renko'\r\n\r\ntype RenkoOptions = Omit<RenkoInput, 'low' | 'close' | 'high' | 'open'>\r\n\r\nexport class Renko extends ChartingSystem {\r\n public options: RenkoOptions\r\n\r\n constructor(options: RenkoOptions) {\r\n super()\r\n this.options = options\r\n }\r\n\r\n public transform(rawData: OHLCV[]): ta.CandleList {\r\n const { opens, highs, lows, closes } = parseIntoRows(rawData)\r\n return ta.renko({\r\n ...this.options,\r\n open: opens,\r\n high: highs,\r\n low: lows,\r\n close: closes,\r\n })\r\n }\r\n}\r\n","import crypto from 'crypto';\r\n\r\nexport const generateMD5Id = (): string => {\r\n const seed = Math.random().toString();\r\n return crypto.createHash('md5').update(seed).digest('hex');\r\n};\r\n","import { PositionType } from '@/types'\r\n\r\nexport function calcPL(\r\n currentClose: number,\r\n openPrice: number,\r\n positionType: PositionType,\r\n): number {\r\n return positionType === PositionType.LONG\r\n ? 100 * ((currentClose - openPrice) / openPrice)\r\n : 100 * ((openPrice - currentClose) / openPrice)\r\n}\r\n","import {\r\n NumberReturnType,\r\n TradeData,\r\n TRADE_KEY,\r\n TradeOptions,\r\n PositionType,\r\n} from '@/types'\r\nimport { generateMD5Id } from '@/utils/generateId'\r\nimport { calcPL } from '@/utils/maths/pl'\r\nimport { OHLCV } from 'ccxt'\r\n\r\nexport class Trade {\r\n public readonly id: string\r\n private readonly tradeData: Map<TRADE_KEY, any>\r\n\r\n constructor(options: TradeOptions) {\r\n this.id = generateMD5Id()\r\n this.tradeData = new Map<TRADE_KEY, any>([\r\n [TRADE_KEY.ID, this.id],\r\n [TRADE_KEY.positionSize, options.positionSize],\r\n [TRADE_KEY.leverage, options.leverage],\r\n [TRADE_KEY.TP, options.TP],\r\n [TRADE_KEY.SL, options.SL],\r\n [TRADE_KEY.PL, 0],\r\n [TRADE_KEY.drawdown, 0],\r\n [TRADE_KEY.closePrice, undefined],\r\n [TRADE_KEY.openPrice, undefined],\r\n [TRADE_KEY.isClosed, false],\r\n [TRADE_KEY.fee, undefined],\r\n [TRADE_KEY.blockChainTrack, undefined],\r\n [TRADE_KEY.timestamp, Date.now()],\r\n [TRADE_KEY.pair, options.pair],\r\n [TRADE_KEY.orderType, options.orderType],\r\n [TRADE_KEY.positionType, options.positionType],\r\n [TRADE_KEY.isOpen, options.open ?? false],\r\n ])\r\n }\r\n\r\n public open(): void {\r\n if (!this.tradeData.get(TRADE_KEY.isClosed)) {\r\n this.tradeData.set(TRADE_KEY.isOpen, true)\r\n }\r\n }\r\n\r\n public close(): void {\r\n this.tradeData.set(TRADE_KEY.isOpen, false)\r\n }\r\n\r\n public updateTP(TP?: number): void {\r\n this.tradeData.set(TRADE_KEY.TP, TP)\r\n }\r\n\r\n public updateSL(SL?: number): void {\r\n this.tradeData.set(TRADE_KEY.SL, SL)\r\n }\r\n\r\n public getData(): TradeData {\r\n return Object.fromEntries(this.tradeData) as TradeData\r\n }\r\n\r\n public onUpdate(update: OHLCV, updates: OHLCV[]): void {\r\n if (this.tradeData.get(TRADE_KEY.isClosed)) return\r\n\r\n const currentClose = Number(update.at(4) ?? 0)\r\n if (!currentClose || currentClose === 0) return\r\n const openPrice = this.tradeData.get(TRADE_KEY.openPrice)\r\n const positionType = this.tradeData.get(TRADE_KEY.positionType)\r\n\r\n if (!openPrice) {\r\n this.tradeData.set(TRADE_KEY.openPrice, currentClose)\r\n return\r\n }\r\n\r\n const pl = calcPL(currentClose, openPrice, positionType)\r\n this.tradeData.set(TRADE_KEY.PL, pl)\r\n this.tradeData.set(TRADE_KEY.closePrice, currentClose)\r\n\r\n const currentDrawdown = this.tradeData.get(TRADE_KEY.drawdown)\r\n if (pl < currentDrawdown) {\r\n this.tradeData.set(TRADE_KEY.drawdown, pl)\r\n }\r\n }\r\n\r\n public getFootprint(): void {}\r\n}\r\n","import { CreateTradeOptions, TRADE_KEY } from '@/types'\r\nimport { Trade } from '@/lib/Trade'\r\nimport { Strategy } from '@/lib/Strategy'\r\nimport { OHLCV } from 'ccxt'\r\n\r\nexport class TradeManager {\r\n private trades = new Map<string, Trade>()\r\n private strategy: Strategy\r\n\r\n constructor(strategy: Strategy) {\r\n this.strategy = strategy\r\n }\r\n\r\n public onUpdate(update: OHLCV, updates: OHLCV[]) {\r\n for (const trade of this.trades.values()) {\r\n if (!trade.getData()[TRADE_KEY.isClosed]) {\r\n trade.onUpdate(update, updates)\r\n }\r\n }\r\n }\r\n\r\n public getTrade(id: string): Trade | null {\r\n return this.trades.get(id) || null\r\n }\r\n\r\n public getTrades(): Trade[] {\r\n return Array.from(this.trades.values())\r\n }\r\n\r\n public createTrade(options: CreateTradeOptions): Trade {\r\n const trade = new Trade({\r\n orderType: options.orderType,\r\n pair: this.strategy.strategyOptions.pair,\r\n positionSize: options.size,\r\n positionType: options.positionType,\r\n leverage: options.leverage ?? 1,\r\n open: options.open ?? false,\r\n SL: options.riskOptions?.SL,\r\n TP: options.riskOptions?.TP,\r\n isLive: false,\r\n })\r\n\r\n this.trades.set(trade.id, trade)\r\n return trade\r\n }\r\n\r\n public getTradeHistory() {\r\n const trades = this.getTrades()\r\n\r\n return trades.filter(async (trade) => {\r\n const tradeData = await trade.getData()\r\n return tradeData[TRADE_KEY.isClosed]\r\n })\r\n }\r\n\r\n public closeTrade(tradeId: string) {\r\n this.getTrade(tradeId)?.close()\r\n }\r\n\r\n public clear() {\r\n this.trades = new Map()\r\n }\r\n}\r\n","/**\r\n * Alpha = R – Rf – beta (Rm-Rf)\r\n R represents the portfolio return.\r\n Rf represents the risk-free rate of return. Beta represents the systematic risk of a portfolio.\r\n Rm represents the market return, per a benchmark.\r\n */\r\n\r\nexport function calcAlpha(r: number, rf: number = 0, beta: number, rm: number) {\r\n return r - rf - beta * (rm - rf)\r\n}\r\n","/**\r\n * covariance divided by the variance\r\n */\r\n\r\nexport function calcBeta(covariance: number, variance: number) {\r\n return covariance / variance\r\n}\r\n","export function calcCovariance(returns: number[], marketReturn: number) {\r\n const meanReturn = returns.reduce((sum, r) => sum + r, 0) / returns.length\r\n return (\r\n returns.reduce(\r\n (cov, r) => cov + (r - meanReturn) * (marketReturn - meanReturn),\r\n 0,\r\n ) / returns.length\r\n )\r\n}\r\n","/**\r\n * Sharpe Ratio = E [Rp-Rf] / σp\r\n\r\nE = Expected value of\r\n\r\nRp = return on a portfolio\r\n\r\nRf = risk-free rate\r\n\r\nσp = standard deviation of the portfolio’s excess return\r\n */\r\n\r\nexport function calcSharpeE(E: number, rp: number, rf: number = 0, sp: number) {\r\n return (E * (rp - rf)) / sp;\r\n}\r\n","export function calcVariance(returns: number[]): number {\r\n const meanReturn = returns.reduce((sum, r) => sum + r, 0) / returns.length\r\n return (\r\n returns.reduce((varSum, r) => varSum + Math.pow(r - meanReturn, 2), 0) /\r\n returns.length\r\n )\r\n}\r\n","import { calcVariance } from './variance'\r\n\r\nexport function calcStddev(returns: number[]): number {\r\n return Math.sqrt(calcVariance(returns))\r\n}\r\n","import exchange from '@/exchange'\r\nimport { CryptoPair, TimeFrame } from '@/types'\r\n\r\nexport default async function getAvgMarketReturn(\r\n pair: CryptoPair,\r\n timeFrame: TimeFrame,\r\n dataLength: number,\r\n): Promise<number> {\r\n const data = await exchange.fetchOHLCV(\r\n pair,\r\n timeFrame,\r\n undefined,\r\n dataLength,\r\n { paginate: true },\r\n )\r\n\r\n let totalReturn = 0\r\n for (let i = 1; i < data.length; i++) {\r\n const prevCandle = data[i-1]\r\n const currCandle = data[i]\r\n \r\n if (!prevCandle || !currCandle) continue\r\n \r\n const prevClose = Number(prevCandle[4])\r\n const currClose = Number(currCandle[4])\r\n const returnPct = ((currClose - prevClose) / prevClose) * 100\r\n totalReturn += returnPct\r\n }\r\n\r\n return totalReturn / (data.length - 1)\r\n}\r\n","import Express from 'express'\r\nimport { BacktestResults, DataSource, DataSourceType } from '@/types'\r\nimport { generateMD5Id } from '@/utils/generateId'\r\nimport path from 'path'\r\nimport { Strategy } from '@/lib'\r\n\r\nexport class ReportManager {\r\n private readonly app = Express()\r\n private readonly PORT = 2555\r\n private readonly viewsPath: string\r\n\r\n constructor() {\r\n this.viewsPath = new URL('./', import.meta.url).pathname\r\n this.initializeApp()\r\n }\r\n\r\n private initializeApp() {\r\n this.app.use(Express.json())\r\n this.app.set('views', this.viewsPath)\r\n this.app.use(Express.static(this.viewsPath))\r\n this.app.listen(this.PORT, () => {\r\n console.log(`Report server listening on port ${this.PORT}`)\r\n })\r\n }\r\n\r\n public generateReport(\r\n strategy: Strategy,\r\n reportData: BacktestResults['reportData'],\r\n ) {\r\n const reportId = generateMD5Id()\r\n const { trades, data } = reportData\r\n const reportRouter = Express.Router()\r\n\r\n const priceData = data.filter((e) => e.type === DataSourceType.PRICE)\r\n const factorData = data.filter((e) => e.type === DataSourceType.FACTOR)\r\n\r\n reportRouter.get('/price-data', (_, res) => {\r\n res.json(priceData)\r\n })\r\n reportRouter.get('/factor-data', (_, res) => {\r\n res.json(factorData)\r\n })\r\n reportRouter.get('/trade-data', (_, res) => {\r\n res.json(trades)\r\n })\r\n reportRouter.get('/', (_, res) => {\r\n const buildPath = path.join(this.viewsPath, 'performance/dist/index.html').replace('\\\\C', 'C')\r\n res.sendFile(buildPath)\r\n })\r\n\r\n this.app.use(`/${reportId}`, reportRouter)\r\n\r\n console.log(\r\n `Report for ${strategy.name}: http://localhost:${this.PORT}/${reportId}`,\r\n )\r\n }\r\n}\r\n","export * from './EMA'\r\nexport * from './SMA'\r\nexport * from './ATR'\r\nexport * from './RSI'\r\nexport * from './MACD'\r\nexport * from './StochRSI'\r\nexport * from './BB'\r\nexport * from './IchimokuCloud'\r\nexport * from './WEMA'\r\nexport * from './ADX'\r\nexport * from './VolumeProfile'\r\nexport * from './VWAP';\r\nexport * from './ForceIndex';","import { Indicator } from '@/lib/Indicator'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class EMA extends Indicator {\r\n public period: number = 9\r\n constructor(key: string, options: EMAOptions) {\r\n super({\r\n name: 'EMA',\r\n key: key,\r\n description: 'Exponential Moving Average.',\r\n })\r\n\r\n if(options.period) this.period = options.period\r\n }\r\n\r\n generate(): number[] {\r\n const values = this.data\r\n .map((update) => update[4])\r\n .filter((value): value is number => value !== undefined)\r\n\r\n const ema = ta.EMA.calculate({\r\n period: this.period,\r\n values: values,\r\n })\r\n\r\n return ema\r\n }\r\n}\r\n\r\nexport interface EMAOptions {\r\n period?: number\r\n}","import { Indicator } from '@/lib/Indicator'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class SMA extends Indicator {\r\n public period: number = 9\r\n constructor(key: string, options: SMAOptions) {\r\n super({\r\n name: 'SMA',\r\n key: key,\r\n description: 'Simple Moving Average.',\r\n })\r\n\r\n if(options.period) this.period = options.period\r\n }\r\n\r\n generate(): number[] {\r\n const values = this.data\r\n .map((update) => update[4])\r\n .filter((value): value is number => value !== undefined)\r\n\r\n const sma = ta.SMA.calculate({\r\n period: this.period,\r\n values: values,\r\n })\r\n\r\n return sma\r\n }\r\n}\r\n\r\nexport interface SMAOptions {\r\n period?: number\r\n}","import { Indicator } from '@/lib/Indicator'\r\nimport { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class ATR extends Indicator {\r\n public period: number = 14\r\n constructor(key: string, options: ATROptions) {\r\n super({\r\n name: 'ATR',\r\n key: key,\r\n description: 'Average True Range.',\r\n })\r\n\r\n if(options.period) this.period = options.period\r\n }\r\n\r\n generate(): number[] {\r\n const { highs, lows, closes } = parseIntoRows(this.data)\r\n\r\n const atr = ta.ATR.calculate({\r\n period: this.period,\r\n high: highs,\r\n low: lows,\r\n close: closes,\r\n })\r\n\r\n return atr\r\n }\r\n}\r\n\r\nexport interface ATROptions {\r\n period?: number\r\n}","import { Indicator } from '@/lib/Indicator'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class RSI extends Indicator {\r\n public period: number = 14\r\n constructor(key: string, options: RSIOptions) {\r\n super({\r\n name: 'RSI',\r\n key: key,\r\n description: 'Relative Strength Index.',\r\n })\r\n\r\n if(options.period) this.period = options.period\r\n }\r\n\r\n generate(): number[] {\r\n const values = this.data\r\n .map((update) => update[4])\r\n .filter((value): value is number => value !== undefined)\r\n\r\n const rsi = ta.RSI.calculate({\r\n period: this.period,\r\n values: values,\r\n })\r\n\r\n return rsi\r\n }\r\n}\r\n\r\nexport interface RSIOptions {\r\n period?: number\r\n}","import { Indicator } from '@/lib/Indicator'\r\nimport ta from 'technicalindicators'\r\nimport { MACDOutput } from 'technicalindicators/declarations/moving_averages/MACD'\r\n\r\nexport class MACD extends Indicator {\r\n public fastPeriod: number = 12\r\n public slowPeriod: number = 26\r\n public signalSmoothing: number = 9\r\n public MAType: 'EMA' | 'SMA' = 'SMA'\r\n public MAOscillatorType: 'EMA' | 'SMA' = 'SMA'\r\n\r\n constructor(key: string, options: MACDOptions) {\r\n super({\r\n name: 'MACD',\r\n key: key,\r\n description: 'Moving Average Convergence/Divergence.',\r\n })\r\n\r\n if (options.fastPeriod) this.fastPeriod = options.fastPeriod\r\n if (options.slowPeriod) this.slowPeriod = options.slowPeriod\r\n if (options.signalSmoothing) this.signalSmoothing = options.signalSmoothing\r\n if (options.MAOscillatorType)\r\n this.MAOscillatorType = options.MAOscillatorType\r\n if (options.MAType) this.MAType = options.MAType\r\n }\r\n\r\n generate(): MACDOutput[] {\r\n const values = this.data\r\n .map((update) => update[4])\r\n .filter((value): value is number => value !== undefined)\r\n\r\n const macd = ta.MACD.calculate({\r\n fastPeriod: this.fastPeriod,\r\n slowPeriod: this.slowPeriod,\r\n SimpleMAOscillator: this.MAOscillatorType === 'SMA' ? true : false,\r\n SimpleMASignal: this.MAType === 'SMA' ? true : false,\r\n signalPeriod: this.signalSmoothing,\r\n values: values,\r\n })\r\n\r\n return macd\r\n }\r\n}\r\n\r\nexport interface MACDOptions {\r\n fastPeriod?: number\r\n slowPeriod?: number\r\n signalSmoothing?: number\r\n MAType?: 'EMA' | 'SMA'\r\n MAOscillatorType?: 'EMA' | 'SMA'\r\n}","import { Indicator } from '@/lib/Indicator'\r\nimport ta from 'technicalindicators'\r\nimport { StochasticRSIOutput } from 'technicalindicators/declarations/momentum/StochasticRSI'\r\n\r\nexport class StockRSI extends Indicator {\r\n public dPeriod: number = 9\r\n public kPeriod: number = 12\r\n public rsiPeriod: number = 14\r\n public stochasticPeriod: number = 16\r\n\r\n constructor(key: string, options: StockRSIOptions) {\r\n super({\r\n name: 'Bollinger Bands',\r\n key: key,\r\n description: 'Boillinger Bands.',\r\n })\r\n\r\n if (options.dPeriod) this.dPeriod = options.dPeriod\r\n if (options.kPeriod) this.kPeriod = options.kPeriod\r\n if (options.rsiPeriod) this.rsiPeriod = options.rsiPeriod\r\n if (options.stochasticPeriod)\r\n this.stochasticPeriod = options.stochasticPeriod\r\n }\r\n\r\n generate(): StochasticRSIOutput[] {\r\n const values = this.data\r\n .map((update) => update[4])\r\n .filter((value): value is number => value !== undefined)\r\n\r\n const stochRSI = ta.StochasticRSI.calculate({\r\n values,\r\n dPeriod: this.dPeriod,\r\n kPeriod: this.kPeriod,\r\n rsiPeriod: this.rsiPeriod,\r\n stochasticPeriod: this.stochasticPeriod,\r\n })\r\n\r\n return stochRSI\r\n }\r\n}\r\n\r\nexport interface StockRSIOptions {\r\n dPeriod: number\r\n kPeriod: number\r\n rsiPeriod: number\r\n stochasticPeriod: number\r\n}\r\n","import { Indicator } from '@/lib/Indicator'\r\nimport ta from 'technicalindicators'\r\nimport { BollingerBandsOutput } from 'technicalindicators/declarations/volatility/BollingerBands'\r\n\r\nexport class BB extends Indicator {\r\n public period: number = 9\r\n public stdDev: number = 1\r\n \r\n constructor(key: string, options: BBOptions) {\r\n super({\r\n name: 'Bollinger Bands',\r\n key: key,\r\n description: 'Boillinger Bands.',\r\n })\r\n\r\n if (options.period) this.period = options.period\r\n if (options.stdDev) this.stdDev = options.stdDev\r\n }\r\n\r\n generate(): BollingerBandsOutput[] {\r\n const values = this.data\r\n .map((update) => update[4])\r\n .filter((value): value is number => value !== undefined)\r\n\r\n const bb = ta.BollingerBands.calculate({\r\n values,\r\n period: this.period,\r\n stdDev: this.stdDev,\r\n })\r\n\r\n return bb\r\n }\r\n}\r\n\r\nexport interface BBOptions {\r\n period?: number\r\n stdDev?: number\r\n}\r\n","import { Indicator } from '@/lib/Indicator'\r\nimport { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport ta from 'technicalindicators'\r\nimport { IchimokuCloudOutput } from 'technicalindicators/declarations/ichimoku/IchimokuCloud'\r\n\r\nexport class IchimokuCloud extends Indicator {\r\n public basePeriod: number = 9\r\n public conversionPeriod: number = 12\r\n public spanPeriod: number = 16\r\n public displacement: number = 21\r\n\r\n constructor(key: string, options: IchimokuCloudOptions) {\r\n super({\r\n name: 'Ichimoku Cloud',\r\n key: key,\r\n description: 'Ichimoku Cloud.',\r\n })\r\n if (options.basePeriod) this.basePeriod = options.basePeriod\r\n if (options.conversionPeriod)\r\n this.conversionPeriod = options.conversionPeriod\r\n if (options.spanPeriod) this.spanPeriod = options.spanPeriod\r\n if (options.displacement) this.displacement = options.displacement\r\n }\r\n\r\n generate(): IchimokuCloudOutput[] {\r\n const { highs, lows } = parseIntoRows(this.data)\r\n\r\n const ichimoku = ta.IchimokuCloud.calculate({\r\n basePeriod: this.basePeriod,\r\n conversionPeriod: this.conversionPeriod,\r\n spanPeriod: this.spanPeriod,\r\n displacement: this.displacement,\r\n high: highs,\r\n low: lows,\r\n })\r\n\r\n return ichimoku\r\n }\r\n}\r\n\r\nexport interface IchimokuCloudOptions {\r\n basePeriod: number\r\n conversionPeriod: number\r\n spanPeriod: number\r\n displacement: number\r\n}\r\n","import { Indicator } from '@/lib/Indicator'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class WEMA extends Indicator {\r\n public period: number = 9\r\n constructor(key: string, options: WEMAOptions) {\r\n super({\r\n name: 'Weighted Exponential Moving Average',\r\n key: key,\r\n description: 'Weighted Exponential Moving Average.',\r\n })\r\n\r\n if(options.period) this.period = options.period\r\n }\r\n\r\n generate(): number[] {\r\n const values = this.data\r\n .map((update) => update[4])\r\n .filter((value): value is number => value !== undefined)\r\n\r\n const wema = ta.WEMA.calculate({\r\n period: this.period,\r\n values: values,\r\n })\r\n\r\n return wema\r\n }\r\n}\r\n\r\nexport interface WEMAOptions {\r\n period?: number\r\n}","import { Indicator } from '@/lib/Indicator'\r\nimport { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport ta from 'technicalindicators'\r\nimport { ADXOutput } from 'technicalindicators/declarations/directionalmovement/ADX'\r\n\r\nexport class ADX extends Indicator {\r\n public period: number = 9\r\n constructor(key: string, options: ADXOptions) {\r\n super({\r\n name: 'ADX',\r\n key: key,\r\n description: 'Trend Strength.',\r\n })\r\n\r\n if (options.period) this.period = options.period\r\n }\r\n\r\n generate(): ADXOutput[] {\r\n const { closes, highs, lows } = parseIntoRows(this.data)\r\n\r\n const adx = ta.ADX.calculate({\r\n period: this.period,\r\n close: closes,\r\n high: highs,\r\n low: lows,\r\n })\r\n\r\n return adx\r\n }\r\n}\r\n\r\nexport interface ADXOptions {\r\n period?: number\r\n}\r\n","import { Indicator } from '@/lib/Indicator'\r\nimport { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class VolumeProfile extends Indicator {\r\n public numberOfBars: number = 20\r\n constructor(key: string, options: VolumeProfileOptions) {\r\n super({\r\n name: 'Volume Profile',\r\n key: key,\r\n description: 'Volume Profile.',\r\n })\r\n\r\n if (options.numberOfBars) this.numberOfBars = options.numberOfBars\r\n }\r\n\r\n generate(): number[] {\r\n const { closes, highs, lows, opens, volumes } = parseIntoRows(this.data)\r\n\r\n const volumeProfile = ta.VolumeProfile.calculate({\r\n close: closes,\r\n high: highs,\r\n low: lows,\r\n open: opens,\r\n volume: volumes,\r\n noOfBars: this.numberOfBars,\r\n })\r\n\r\n return volumeProfile\r\n }\r\n}\r\n\r\nexport interface VolumeProfileOptions {\r\n numberOfBars?: number\r\n}\r\n","import { Indicator } from '@/lib/Indicator'\r\nimport { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class VWAP extends Indicator {\r\n constructor(key: string) {\r\n super({\r\n name: 'VWAP',\r\n key: key,\r\n description: 'Volume-weighted Average Price.',\r\n })\r\n }\r\n\r\n generate(): number[] {\r\n const { closes, highs, lows, opens, timestamps, volumes } = parseIntoRows(\r\n this.data,\r\n )\r\n\r\n const vwap = ta.VWAP.calculate({\r\n close: closes,\r\n high: highs,\r\n low: lows,\r\n volume: volumes,\r\n })\r\n\r\n return vwap\r\n }\r\n}\r\n","import { Indicator } from '@/lib/Indicator'\r\nimport { parseIntoRows } from '@/utils/parseOHLCV'\r\nimport ta from 'technicalindicators'\r\n\r\nexport class ForceIndex extends Indicator {\r\n public period: number = 9\r\n constructor(key: string, options: ForceIndexOptions) {\r\n super({\r\n name: 'ForceIndex',\r\n key: key,\r\n description: 'Force Index.',\r\n })\r\n\r\n if (options.period) this.period = options.period\r\n }\r\n\r\n generate(): number[] {\r\n const { closes, volumes } = parseIntoRows(this.data)\r\n\r\n const forceIndex = ta.ForceIndex.calculate({\r\n period: this.period,\r\n close: closes,\r\n volume: volumes,\r\n })\r\n\r\n return forceIndex\r\n }\r\n}\r\n\r\nexport interface ForceIndexOptions {\r\n period: number\r\n}\r\n"],"mappings":"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