sec-edgar-api
Version:
Fetch and parse SEC earnings reports and other filings. Useful for financial analysis.
223 lines (222 loc) • 10 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseForm4 = void 0;
var XMLParserLegacy_1 = require("../XMLParserLegacy");
/**
* Form 4 - Insider Transactions
*
* example at https://www.sec.gov/Archives/edgar/data/320193/000032019323000079/xslF345X05/wk-form4_1691533817.xml
*/
function parseForm4(params, xmlParser) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
if (xmlParser === void 0) { xmlParser = new XMLParserLegacy_1.default(); }
var xml = params.xml;
var textMap = xmlParser.getTableTextMap({ xml: xml, parentPath: 'html.body' });
var getTextBetween = function (text, before, after) {
var indexBefore = text.indexOf(before);
var indexAfter = after ? text.indexOf(after, indexBefore) : text.length;
return text.substring(indexBefore + before.length, indexAfter).trim();
};
var relationText = (_a = textMap.get('2.1.3')) !== null && _a !== void 0 ? _a : '';
var filerNameText = (_b = textMap.get('2.1.1')) !== null && _b !== void 0 ? _b : '';
var filterNameText = getTextBetween(filerNameText, '1. Name and Address of Reporting Person*', '(Last)');
var isDirector = relationText.substring(0, relationText.indexOf('Director')).includes('X');
var isOwner10 = getTextBetween(relationText, 'Director', '10% Owner').includes('X');
var isOfficer = getTextBetween(relationText, '10% Owner', 'Officer (give title below)').includes('X');
var isOther = getTextBetween(relationText, 'Officer (give title below)', 'Other (specify below)').includes('X');
var position = getTextBetween(relationText, 'Other (specify below)');
var filerPositionTypes = [];
if (isDirector)
filerPositionTypes.push('Director');
if (isOwner10)
filerPositionTypes.push('10% Owner');
if (isOfficer)
filerPositionTypes.push('Officer');
if (isOther)
filerPositionTypes.push('Other');
var codeTranslations = {
P: 'Purchase',
S: 'Sale',
V: 'Voluntary Reporting',
A: 'Grant',
D: 'Sale to Issuer',
F: 'Payment of Exercise Price',
I: 'Discretionary Transaction',
M: 'Conversion of Derivative Exempt',
C: 'Conversion of Derivative',
E: 'Expiration of Short Derivative Position',
H: 'Expiration of Long Derivative Position',
O: 'Exercise of out-of-the-money Derivative',
X: 'Exercise of in-the-money Derivative',
G: 'Gift',
L: 'Small Acquisition',
W: 'Acquisition or Disposition By Will or Laws',
Z: 'Voting Trust Deposit or Withdrawal',
J: 'Other Acquisition or Disposition',
K: 'Equity Swap',
U: 'Disposition Change in Control',
};
var toDate = function (str) {
if (str === '')
return '';
var _a = str.split('/'), month = _a[0], day = _a[1], year = _a[2];
return [month, day, year].some(function (x) { return x === undefined; }) ? '' : "".concat(year, "-").concat(month, "-").concat(day);
};
var createTransaction = function () { return ({
filerName: filterNameText,
filerPosition: position,
filerPositionTypes: filerPositionTypes,
category: 'Non-Derivative',
securityType: '',
securityTypeUnderlying: null,
date: '',
dateExecuted: null,
dateExpiration: null,
dateExercisable: null,
transactionType: null,
transactionCode: null,
transactionDescription: null,
price: null,
priceExcercised: null,
shares: null,
sharesUnderlying: null,
sharesEnding: null,
ownership: '',
explainationByKey: {},
}); };
var getColText = function (colKey) {
var _a, _b;
var text = (_a = textMap.get(colKey)) === null || _a === void 0 ? void 0 : _a.replace(/\(\d+\)|\$/g, '');
return (_b = text === null || text === void 0 ? void 0 : text.trim()) !== null && _b !== void 0 ? _b : '';
};
var headingNonDerivative = [
'securityType',
'date',
'dateExecuted',
'transactionCode',
'',
'shares',
'transactionType',
'price',
'sharesEnding',
'ownership',
];
var headingDerivative = [
'securityType',
'priceExcercised',
'date',
'dateExecuted',
'transactionCode',
'',
'shares',
'shares',
'dateExercisable',
'dateExpiration',
'securityTypeUnderlying',
'sharesUnderlying',
'price',
'sharesEnding',
'ownership',
];
var maxIterations = 10000;
var transactions = [];
transactionsNonDerivative: for (var row = 4; row < maxIterations; row++) {
var transaction = createTransaction();
transaction.category = 'Non-Derivative';
// get all non-derivative transactions
for (var col = 1; col < 11; col++) {
var colName = ((_c = headingNonDerivative[col - 1]) !== null && _c !== void 0 ? _c : '');
var colKey = "3.".concat(row, ".").concat(col);
var text = getColText(colKey);
var explanationNum = (_f = (_e = ((_d = textMap.get(colKey)) !== null && _d !== void 0 ? _d : '').match(/(?<=\()\d+(?=\))/g)) === null || _e === void 0 ? void 0 : _e[0]) !== null && _f !== void 0 ? _f : null;
var explanationText = explanationNum ? (_g = textMap.get("5.".concat(Number(explanationNum) + 1, ".1"))) !== null && _g !== void 0 ? _g : '' : null;
if (colName === '')
continue;
if (explanationText !== null)
transaction.explainationByKey[colName] = explanationText.trim();
if (!textMap.has(colKey))
break transactionsNonDerivative;
switch (colName) {
case 'transactionType':
transaction.transactionType = text === 'A' ? 'Acquire' : 'Dispose';
continue;
case 'transactionCode':
transaction.transactionDescription = (_h = codeTranslations[text]) !== null && _h !== void 0 ? _h : '';
transaction.transactionCode = text;
continue;
case 'date':
transaction.date = toDate(text);
continue;
case 'dateExecuted':
case 'dateExercisable':
case 'dateExpiration':
transaction[colName] = toDate(text) || null;
continue;
case 'price':
case 'shares':
case 'sharesEnding': {
var valueNum = Number(text.replace(/,/g, ''));
transaction[colName] = text === '' || isNaN(valueNum) ? null : valueNum;
continue;
}
default:
transaction[colName] = text;
}
}
transactions.push(transaction);
}
transactionsDerivative: for (var row = 4; row < maxIterations; row++) {
var transaction = createTransaction();
transaction.category = 'Derivative';
var textSharesAcquired = getColText("4.".concat(row, ".6"));
var textSharesDisposed = getColText("4.".concat(row, ".7"));
var sharesAcquired = Number(textSharesAcquired);
var sharesDisposed = Number(textSharesDisposed);
if (textSharesAcquired !== '' || textSharesDisposed !== '') {
transaction.transactionType = sharesAcquired - sharesDisposed > 0 ? 'Acquire' : 'Dispose';
}
for (var col = 1; col < 16; col++) {
var colName = ((_j = headingDerivative[col - 1]) !== null && _j !== void 0 ? _j : '');
var colKey = "4.".concat(row, ".").concat(col);
var text = ((_k = textMap.get(colKey)) !== null && _k !== void 0 ? _k : '').replace(/\(\d+\)|\$/g, '').trim();
var explanationNum = (_o = (_m = ((_l = textMap.get(colKey)) !== null && _l !== void 0 ? _l : '').match(/(?<=\()\d+(?=\))/g)) === null || _m === void 0 ? void 0 : _m[0]) !== null && _o !== void 0 ? _o : null;
var explanationText = explanationNum ? (_p = textMap.get("5.".concat(Number(explanationNum) + 1, ".1"))) !== null && _p !== void 0 ? _p : '' : null;
if (colName === '')
continue;
if (explanationText !== null)
transaction.explainationByKey[colName] = explanationText.trim();
if (!textMap.has(colKey))
break transactionsDerivative;
switch (colName) {
case 'transactionType':
transaction.transactionType = text === 'A' ? 'Acquire' : 'Dispose';
continue;
case 'transactionCode':
transaction.transactionDescription = (_q = codeTranslations[text]) !== null && _q !== void 0 ? _q : '';
transaction.transactionCode = text;
continue;
case 'dateExecuted':
case 'dateExercisable':
case 'dateExpiration':
transaction[colName] = toDate(text) || null;
continue;
case 'price':
case 'shares':
case 'sharesUnderlying':
case 'priceExcercised':
case 'sharesEnding': {
if (colName === 'shares' && transaction.shares !== null)
continue;
var valueNum = Number(text.replace(/,/g, ''));
transaction[colName] = text === '' || isNaN(valueNum) ? null : valueNum;
continue;
}
default:
transaction[colName] = text;
}
}
transactions.push(transaction);
}
return { transactions: transactions };
}
exports.parseForm4 = parseForm4;