Skip to content

Instantly share code, notes, and snippets.

@szxmsu
Last active January 1, 2022 11:54
Show Gist options
  • Save szxmsu/7676f14a489c89683d31b57d079298ca to your computer and use it in GitHub Desktop.
Save szxmsu/7676f14a489c89683d31b57d079298ca to your computer and use it in GitHub Desktop.
const fs = require('fs');
const path = require('path');
const utility = require('./utility');
// region Constants.
const DATA_FILE_PATH = path.join(__dirname, '..', 'fund-data');
const DATE_RANGES = {
SIX_MONTHS: 'SIX_MONTHS',
ONE_YEAR: 'ONE_YEAR',
TWO_YEARS: 'TWO_YEARS',
THREE_YEARS: 'THREE_YEARS',
};
const ACTIONS = {
ANALYZE: 'analyze',
CALCULATE: 'calculate',
};
const FORMATTER = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
// endregion
// region Functions.
const getCurrentFundsFilePath = (year) => {
const fileName = `funds-${year}-07.json`;
return path.join(DATA_FILE_PATH, fileName);
};
const getFutureFundsFilePath = (year, dateRange) => {
let fileName = '';
switch (dateRange) {
case DATE_RANGES.SIX_MONTHS:
fileName = `funds-${+year + 1}-01.json`;
break;
case DATE_RANGES.ONE_YEAR:
fileName = `funds-${+year + 1}-07.json`;
break;
case DATE_RANGES.TWO_YEARS:
fileName = `funds-${+year + 2}-07.json`;
break;
case DATE_RANGES.THREE_YEARS:
fileName = `funds-${+year + 3}-07.json`;
break;
default:
throw new Error(`Unknown date range: ${dateRange}.`);
}
return path.join(DATA_FILE_PATH, fileName);
};
const readFileSync = (filePath) => {
let resultString = null;
if (fs.existsSync(filePath)) {
resultString = fs.readFileSync(filePath, { encoding: 'utf8' });
}
return JSON.parse(resultString);
};
const writeFileSync = (filePath, data) => {
fs.writeFileSync(filePath, JSON.stringify(data), { encoding: 'utf8' });
};
const analyzeFunds = (initialFunds, year) => {
const fundStruct = {
code: null,
name: null,
type: null,
sixMonths: null,
oneYear: null,
twoYears: null,
threeYears: null,
};
const averageStruct = {
sixMonths: null,
oneYear: null,
twoYears: null,
threeYears: null,
};
const analyzedFunds = utility.analyzeFunds(initialFunds, 'strict');
const analyticalResult = {
year,
month: '07',
mode: 'strict',
allowedTypes: null,
analyzedFunds: null,
initialAverages: null,
analyzedAverages: null,
initialQuantities: null,
analyzedQuantities: null,
};
analyticalResult.allowedTypes = utility.getAllowedTypes();
analyticalResult.analyzedFunds = utility.initializeFundResults(analyzedFunds, fundStruct);
analyticalResult.initialAverages = utility.initializeAverageResults(initialFunds, averageStruct);
analyticalResult.analyzedAverages = utility.initializeAverageResults(analyzedFunds, averageStruct);
analyticalResult.initialQuantities = utility.convertToQuantitiesWithType(initialFunds);
analyticalResult.analyzedQuantities = utility.convertToQuantitiesWithType(analyzedFunds);
return analyticalResult;
};
const calculateFunds = (currentFunds, futureFunds, analyticalResult, options) => {
const flattedFutureFunds = Object.values(futureFunds).flat();
analyticalResult.allowedTypes.forEach((type) => {
analyticalResult.analyzedFunds[type].forEach((analyzedFund) => {
const index = flattedFutureFunds
.findIndex((futureFund) => futureFund.code === analyzedFund.code);
if (index !== -1) {
// eslint-disable-next-line no-param-reassign
analyzedFund[options.analyticalField] = flattedFutureFunds[index][options.initialField];
}
});
let incomeBeforeAnalyzing = 0;
let quantityBeforeAnalyzing = 0;
flattedFutureFunds.forEach((futureFund) => {
const index = currentFunds[type]
.findIndex((currentFund) => currentFund.code === futureFund.code);
if (index !== -1) {
incomeBeforeAnalyzing += futureFund[options.initialField];
quantityBeforeAnalyzing += 1;
}
});
let incomeAfterAnalyzing = 0;
let quantityAfterAnalyzing = 0;
flattedFutureFunds.forEach((futureFund) => {
const index = analyticalResult.analyzedFunds[type]
.findIndex((analyzedFund) => analyzedFund.code === futureFund.code);
if (index !== -1) {
incomeAfterAnalyzing += futureFund[options.initialField];
quantityAfterAnalyzing += 1;
}
});
const averageBeforeAnalyzing = +FORMATTER.format(
incomeBeforeAnalyzing / quantityBeforeAnalyzing,
);
const averageAfterAnalyzing = +FORMATTER.format(
incomeAfterAnalyzing / quantityAfterAnalyzing,
);
// eslint-disable-next-line no-param-reassign
analyticalResult.initialAverages[type][options.analyticalField] = averageBeforeAnalyzing;
// eslint-disable-next-line no-param-reassign
analyticalResult.analyzedAverages[type][options.analyticalField] = averageAfterAnalyzing;
});
};
// endregion
// region Main.
const main = () => {
const args = process.argv.slice(2);
const [action, year] = args;
if (action === undefined || year === undefined) {
console.error('Usage: node calculate-funds.js <action> <year>.');
console.error(' action: analyze | calculate');
console.error(' year: the year to analyze or calculate');
process.exit(1);
}
const currentFunds = readFileSync(getCurrentFundsFilePath(year));
if (currentFunds === null) {
console.error(`Could not read file: ${getCurrentFundsFilePath(year)}.`);
process.exit(1);
}
const analyticalResultsFileName = 'analytical-results.json';
const analyticalResultsFilePath = path.join(DATA_FILE_PATH, analyticalResultsFileName);
if (action === ACTIONS.ANALYZE) {
const analyticalResults = readFileSync(analyticalResultsFilePath) || [];
const analyticalResult = analyzeFunds(currentFunds, year);
const index = analyticalResults.findIndex((result) => result.year === year);
if (index !== -1) {
analyticalResults.splice(index, 1, analyticalResult);
} else {
analyticalResults.push(analyticalResult);
}
analyticalResults.sort((x, y) => +y.year - +x.year);
writeFileSync(analyticalResultsFilePath, analyticalResults);
}
if (action === ACTIONS.CALCULATE) {
const futureFundsArray = [
readFileSync(getFutureFundsFilePath(year, DATE_RANGES.SIX_MONTHS)),
readFileSync(getFutureFundsFilePath(year, DATE_RANGES.ONE_YEAR)),
readFileSync(getFutureFundsFilePath(year, DATE_RANGES.TWO_YEARS)),
readFileSync(getFutureFundsFilePath(year, DATE_RANGES.THREE_YEARS)),
];
const optionsArray = [{
initialField: 'lastSixMonths',
analyticalField: 'sixMonths',
}, {
initialField: 'lastOneYear',
analyticalField: 'oneYear',
}, {
initialField: 'lastTwoYears',
analyticalField: 'twoYears',
}, {
initialField: 'lastThreeYears',
analyticalField: 'threeYears',
}];
const analyticalResults = readFileSync(analyticalResultsFilePath) || [];
const analyticalResult = analyticalResults.find((result) => result.year === year);
futureFundsArray.forEach((futureFunds, index) => {
const options = optionsArray[index];
if (futureFunds !== null) {
calculateFunds(currentFunds, futureFunds, analyticalResult, options);
}
});
writeFileSync(analyticalResultsFilePath, analyticalResults);
}
};
// endregion
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment