├── .gitignore ├── LICENSE.md ├── README.md ├── VanguardSectorETFs ├── VAW.csv ├── VCR.csv ├── VDC.csv ├── VDE.csv ├── VFH.csv ├── VGT.csv ├── VHT.csv ├── VIS.csv ├── VNQ.csv ├── VOX.csv └── VPU.csv ├── main.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 KibaeKim 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SectorTradingAlgorithm 2 | 3 | This is a simple trading algorithm I discovered that operates on the Vanguard sector ETFs. This backdating algorithm provides on average a return of ~0.0878% for each trading day, or ~22.20% annualized return assuming 253 trading days per year. 4 | 5 | ## The Algorithm 6 | 7 | This algorithm is really simple. 8 | 1. On day `n`, determine which ETF gave the highest return 9 | 2. On day `n+1`, short sell the previous day's highest performing ETF at market open and close your short position at market close. 10 | 11 | Because this algorithm operates on Vanguard's 11 Sector ETFs, it is resilient against the volatility of individual stocks. 12 | 13 | ### Caution 14 | Hindsight is 20/20 and because this is a backdating algorithm, similar results are not guaranteed in the future. Use at your own risk. 15 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | const csv = require('csv-parser'); 2 | const fs = require('fs'); 3 | 4 | const etfDirectory = 'VanguardSectorETFs'; 5 | 6 | var res = {}; 7 | 8 | fs.readdir(etfDirectory, function (err, files) { 9 | if (err) { 10 | console.log('Could not list the directory.', err); 11 | process.exit(1); 12 | } 13 | 14 | // Reading each csv file in folder VanguardSectorETFs 15 | files.forEach((file, index) => { 16 | fs.createReadStream(`${etfDirectory}/${file}`) 17 | .pipe(csv()) 18 | .on('data', (data) => { 19 | var etf = file.substring(0, 3); 20 | var date = data.Date; 21 | var open = data.Open; 22 | var close = data.Close; 23 | 24 | // Making a new array if this is the first piece of data on this date 25 | if (!res[date]) { 26 | res[date] = []; 27 | } 28 | res[date].push({ name: etf, open: open, close: close }); 29 | }) 30 | .on('end', () => { 31 | // Run mainFunction() after reading the last file 32 | if (index == files.length - 1) { 33 | mainFunction(); 34 | } 35 | }); 36 | }); 37 | }); 38 | 39 | function mainFunction() { 40 | bestPerformers = []; 41 | Object.keys(res) 42 | .sort() 43 | .forEach((date, index) => { 44 | var bestPerformance = Number.NEGATIVE_INFINITY; 45 | var bestEtf = ''; 46 | res[date].forEach((etf) => { 47 | etfPerformance = (etf.close - etf.open) / etf.open; 48 | if (etfPerformance > bestPerformance) { 49 | bestEtf = etf.name; 50 | bestPerformance = etfPerformance; 51 | } 52 | }); 53 | bestPerformers.push(bestEtf); 54 | }); 55 | 56 | performance = []; 57 | cash = 1000.0; 58 | Object.keys(res) 59 | .sort() 60 | .forEach((date, index) => { 61 | if (index != 0) { 62 | res[date].forEach((etf) => { 63 | if (etf.name == bestPerformers[index - 1]) { 64 | cash = cash * (1 + (etf.open - etf.close) / etf.close); 65 | performance.push((etf.open - etf.close) / etf.close); 66 | } 67 | }); 68 | } 69 | }); 70 | 71 | var total = 0; 72 | for (var i = 0; i < performance.length; i++) { 73 | total += performance[i]; 74 | } 75 | var avg = total / performance.length; 76 | var tradingDays = performance.length; 77 | 78 | var annualizedReturn = (cash / 1000) ** (253 / tradingDays) - 1; 79 | 80 | /* 81 | Average daily rate = 0.08774690684042068% 82 | Number of trading days = 4366 83 | Annualized return = 22.204163102061415% 84 | 1000 on January 30, 2004 would equal 31829.94540714155 on June 6, 2021 85 | Standard deviation = 0.013087746861928531 86 | Performance on the worst trading day -9.825416558076196% 87 | */ 88 | console.log(`Average daily rate = ${avg * 100}%`); 89 | console.log(`Number of trading days = ${tradingDays}`); 90 | console.log(`Annualized return = ${annualizedReturn * 100}%`); 91 | console.log( 92 | `${1000} on January 30, 2004 would equal ${cash} on June 6, 2021` 93 | ); 94 | console.log( 95 | `Standard deviation = ${calculateStandardDeviation(performance)}` 96 | ); 97 | console.log( 98 | `Performance on the worst trading day ${ 99 | worstDayPerformance(performance) * 100 100 | }%` 101 | ); 102 | } 103 | 104 | function calculateStandardDeviation(performance) { 105 | var total = 0; 106 | for (var key in performance) total += performance[key]; 107 | var meanVal = total / performance.length; 108 | 109 | var sdPrep = 0; 110 | for (var key in performance) 111 | sdPrep += Math.pow(parseFloat(performance[key]) - meanVal, 2); 112 | var sdResult = Math.sqrt(sdPrep / performance.length); 113 | return sdResult; 114 | } 115 | 116 | function worstDayPerformance(performance) { 117 | return Math.min(...performance); 118 | } 119 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "etfalgorithm", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "csv-parser": { 8 | "version": "3.0.0", 9 | "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", 10 | "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", 11 | "requires": { 12 | "minimist": "^1.2.0" 13 | } 14 | }, 15 | "minimist": { 16 | "version": "1.2.5", 17 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", 18 | "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "etfalgorithm", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "csv-parser": "^3.0.0" 13 | } 14 | } 15 | --------------------------------------------------------------------------------