Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@davidosborn/crypto-tax-calculator

Package Overview
Dependencies
Maintainers
1
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@davidosborn/crypto-tax-calculator - npm Package Compare versions

Comparing version 0.0.14 to 0.0.15

lib/csv-normalize-stream.js

45

lib/capital-gains-calculate-stream.js

@@ -36,3 +36,3 @@ 'use strict';

* @typedef {object} CapitalGains
* @property {Map.<string, Forward>} forwardByAsset The assets that were carried forward from last year.
* @property {Map.<string, Forward>} [forwardByAsset] The assets that were carried forward from last year.
* @property {array.<Trade>} trades The trades.

@@ -57,4 +57,5 @@ * @property {Map.<string, Ledger>} ledgerByAsset The ledger of each asset.

* Initializes a new instance.
* @param {object} [options] The options.
* @param {object.<string, Forward>} [options.forwardByAsset] The assets to carry forward from last year.
* @param {object} [options] The options.
* @param {Set.<string>} [options.assets] The assets to retain.
* @param {Map.<string, Forward>} [options.forwardByAsset] The assets to carry forward from last year.
*/

@@ -70,6 +71,6 @@ constructor(options) {

* The assets to carry forward from last year.
* @type {Map.<string, Ledger>}
* @type {Map.<string, Forward>}
*/
this._forwardByAsset = new Map(((_this$_options = this._options) === null || _this$_options === void 0 ? void 0 : _this$_options.forwardByAsset) ? Object.entries(this._options.forwardByAsset) : []);
this._forwardByAsset = (_this$_options = this._options) === null || _this$_options === void 0 ? void 0 : _this$_options.forwardByAsset;
/**

@@ -94,11 +95,7 @@ * The trades.

if (this._forwardByAsset) {
for (let [asset, forward] of this._forwardByAsset) {
this._ledgerByAsset.set(asset, {
acb: forward.acb,
balance: forward.balance,
dispositions: []
});
}
}
if (this._forwardByAsset) for (let [asset, forward] of this._forwardByAsset.entries()) this._ledgerByAsset.set(asset, {
acb: forward.acb,
balance: forward.balance,
dispositions: []
});
}

@@ -125,6 +122,3 @@ /**

if (chunk.amount < 0) {
if (!ledger.balance) {
console.log('WARNING: Disposition of ' + chunk.asset + ' from an empty balance on ' + (0, _formatTime.default)(chunk.time) + '.');
}
if (!ledger.balance) console.log('WARNING: Disposition of ' + chunk.asset + ' from an empty balance on ' + (0, _formatTime.default)(chunk.time) + '.');
let acbPerUnit = ledger.balance ? ledger.acb / ledger.balance : 0;

@@ -145,3 +139,3 @@ let disposition = {

ledger.balance += chunk.amount; // Remove the transaction fee from the balance.
ledger.balance += chunk.amount; // Remove the transaction fee from the balance, except for fiat currencies.

@@ -156,6 +150,7 @@ if (chunk.feeAmount) {

}
} // Record the balance in the trade.
} // Record the balance and ACB in the trade for information purposes.
chunk.balance = ledger.balance; // Check whether the balance is negative, which would indicate an accounting error.
chunk.balance = ledger.balance;
chunk.acb = ledger.acb; // Check whether the balance is negative, which would indicate an accounting error.

@@ -165,5 +160,7 @@ if (ledger.balance < -0.000000005 && !this._assetsWithNegativeBalance.has(chunk.asset)) {

console.log('WARNING: Encountered a negative balance for ' + chunk.asset + '.');
}
console.log('WARNING: Encountered a negative balance for ' + chunk.asset + ' on ' + (0, _formatTime.default)(chunk.time) + '.');
} // Clear the ACB when the balance is negative.
if (ledger.balance <= 0) ledger.acb = 0;
callback();

@@ -212,3 +209,3 @@ }

ledgerByAsset: this._ledgerByAsset,
aggregateDisposition: aggregateDisposition,
aggregateDisposition,
taxableGain: aggregateDisposition.gain / 2 // Capital gains are taxable at 50%.

@@ -215,0 +212,0 @@

@@ -10,6 +10,8 @@ 'use strict';

var _marked = _interopRequireDefault(require("marked"));
var _stream = _interopRequireDefault(require("stream"));
var _assets = _interopRequireDefault(require("./assets"));
var _formatTime = _interopRequireDefault(require("./format-time"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -53,4 +55,6 @@

_transform(chunk, encoding, callback) {
var _chunk$forwardByAsset;
// Write the balance that was carried forward from last year.
if (chunk.forwardByAsset.size) {
if ((_chunk$forwardByAsset = chunk.forwardByAsset) === null || _chunk$forwardByAsset === void 0 ? void 0 : _chunk$forwardByAsset.size) {
this._pushLine();

@@ -72,3 +76,3 @@

this._pushLine((0, _markdownTable.default)([['Asset', 'Units acquired', 'Value', 'Balance', 'Fee', 'Fee asset', 'Date', 'Exchange']].concat(Array.from(chunk.trades, trade => [trade.asset, this._formatAmount(trade.amount), this._formatValue(trade.value), this._formatAmount(trade.balance), this._formatAmount(trade.feeAmount), trade.feeAsset, this._formatDate(trade.time), trade.exchange])))); // Sort the assets.
this._pushLine((0, _markdownTable.default)([['Asset', 'Units acquired (or disposed)', 'Value', 'Balance', 'Adjusted cost base', 'Fee', 'Fee asset', 'Time', 'Exchange']].concat(Array.from(chunk.trades, trade => [trade.asset, this._formatAmount(trade.amount), this._formatValue(trade.value), this._formatAmount(trade.balance), this._formatValue(trade.acb), trade.feeAsset ? this._formatAssetAmount(trade.feeAsset, trade.feeAmount) : '', trade.feeAsset ? trade.feeAsset : '', (0, _formatTime.default)(trade.time), trade.exchange])))); // Sort the assets.

@@ -87,3 +91,3 @@

this._pushLine((0, _markdownTable.default)([['Asset', 'Units', 'Proceeds of disposition', 'Adjusted cost base', 'Outlays and expenses', 'Gain (or loss)', 'Date', 'Exchange']].concat(...ledgerByAsset.map(([asset, ledger]) => Array.from(ledger.dispositions, disposition => [asset, this._formatAmount(disposition.amount), this._formatValue(disposition.pod), this._formatValue(disposition.acb), this._formatValue(disposition.oae), this._formatValue(disposition.gain), this._formatDate(disposition.time), disposition.exchange]))))); // Find the assets with dispositions.
this._pushLine((0, _markdownTable.default)([['Asset', 'Units', 'Proceeds of disposition', 'Adjusted cost base', 'Outlays and expenses', 'Gain (or loss)', 'Time', 'Exchange']].concat(...ledgerByAsset.map(([asset, ledger]) => Array.from(ledger.dispositions, disposition => [asset, this._formatAmount(disposition.amount), this._formatValue(disposition.pod), this._formatValue(disposition.acb), this._formatValue(disposition.oae), this._formatValue(disposition.gain), (0, _formatTime.default)(disposition.time), disposition.exchange]))))); // Find the assets with dispositions.

@@ -155,2 +159,6 @@

_formatAssetAmount(asset, amount) {
return _assets.default.getPriority(asset) !== 0 ? this._formatAmount(amount) : this._formatValue(amount);
}
_formatAmount(amount) {

@@ -170,13 +178,2 @@ let amountString = this._amountFormat.format(Math.abs(amount));

_formatDate(time) {
return new Date(time).toLocaleDateString('en-CA', {
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
month: 'short',
second: '2-digit',
year: 'numeric'
});
}
_pushLine(line) {

@@ -183,0 +180,0 @@ this.push(line);

@@ -19,3 +19,3 @@ 'use strict';

minute: '2-digit',
month: '2-digit',
month: 'short',
year: 'numeric'

@@ -22,0 +22,0 @@ });

@@ -18,2 +18,4 @@ 'use strict';

var _lineStream = _interopRequireDefault(require("line-stream"));
var _multistream = _interopRequireDefault(require("multistream"));

@@ -39,2 +41,6 @@

var _csvNormalizeStream = _interopRequireDefault(require("./csv-normalize-stream"));
var _tradeFilterStream = _interopRequireDefault(require("./trade-filter-stream"));
var _tradeParseStream = _interopRequireDefault(require("./trade-parse-stream"));

@@ -46,4 +52,2 @@

var _transactionFilterStream = _interopRequireDefault(require("./transaction-filter-stream"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -62,3 +66,3 @@

argument: 'spec',
description: 'Only consider the specified assets.'
description: 'Only consider trades involving the specified assets.'
}, {

@@ -77,3 +81,3 @@ short: 'h',

long: 'html',
description: 'Format the output as HTML instead of Markdown.'
description: 'Format the results as HTML instead of Markdown.'
}, {

@@ -83,7 +87,7 @@ short: 'o',

argument: 'file',
description: 'Write the output to the specified file.'
description: 'Write the results to the specified file.'
}, {
short: 'q',
long: 'quiet',
description: 'Do not produce any output.'
description: 'Do not write the results.'
}, {

@@ -124,13 +128,20 @@ short: 't',

if (!('html' in opts.options) && ((destination === null || destination === void 0 ? void 0 : destination.endsWith('.html')) || (destination === null || destination === void 0 ? void 0 : destination.endsWith('.htm')))) opts.options.html = true; // Parse the initial balance and ACB of each asset to carry it forward from last year.
if (!('html' in opts.options) && ((destination === null || destination === void 0 ? void 0 : destination.endsWith('.html')) || (destination === null || destination === void 0 ? void 0 : destination.endsWith('.htm')))) opts.options.html = true; // Parse the assets to retain when filtering the trades.
let forwardByAsset = null;
if (opts.options.init) forwardByAsset = (0, _fromentries.default)(opts.options.init.value.split(',').map(function (spec) {
let [asset, balance, acb] = spec.split(':');
return [asset, {
balance: parseFloat(balance),
acb: parseFloat(acb)
}];
})); // Load the historical data.
let assets = undefined;
if (opts.options.assets) assets = new Set(opts.options.assets.value.split(',').map(_assets.default.normalizeCode)); // Parse the initial balance and ACB of each asset to carry it forward from last year.
let forwardByAsset = undefined;
if (opts.options.init) {
forwardByAsset = new Map(opts.options.init.value.split(',').map(function (spec) {
let [asset, balance, acb] = spec.split(':');
return [asset, {
balance: parseFloat(balance),
acb: parseFloat(acb)
}];
}));
} // Load the historical data.
let historyPath = (_ref = (_opts$options$history = opts.options.history) === null || _opts$options$history === void 0 ? void 0 : _opts$options$history.value) !== null && _ref !== void 0 ? _ref : __dirname + '/../history';

@@ -141,3 +152,3 @@ let history = historyPath ? (0, _loadHistory.default)(historyPath) : null; // Create a stream to calculate the capital gains.

let stream = (0, _mergeSortStream.default)(_compareTradeTime, sources.map(function (path) {
return _fs.default.createReadStream(path).pipe((0, _toUtf.default)()).pipe((0, _csvParse.default)({
return _fs.default.createReadStream(path).pipe((0, _toUtf.default)()).pipe((0, _lineStream.default)()).pipe((0, _csvNormalizeStream.default)()).pipe((0, _csvParse.default)({
columns: true,

@@ -148,14 +159,14 @@ skip_empty_lines: true

if (opts.options.take) stream = stream.pipe((0, _takeStream.default)(parseInt(opts.options.take.value)));
stream = stream.pipe((0, _tradeValueStream.default)({
if (opts.options.take) stream = stream.pipe((0, _takeStream.default)(parseInt(opts.options.take.value))); // Filter the trades by their assets.
if (assets) stream = stream.pipe((0, _tradeFilterStream.default)({
assets
})); // Calculate the capital gains.
stream = (0, _multistream.default)([_fs.default.createReadStream(__dirname + '/../res/output_header.md'), stream.pipe((0, _tradeValueStream.default)({
history,
verbose: !!opts.options.verbose,
web: !!opts.options.web
})).pipe((0, _tradeTransactionsStream.default)()); // Limit the assets.
if (opts.options.assets) stream = stream.pipe((0, _transactionFilterStream.default)({
assets: new Set(opts.options.assets.value.split(',').map(_assets.default.normalizeCode))
})); // Calculate the capital gains.
stream = (0, _multistream.default)([_fs.default.createReadStream(__dirname + '/../res/output_header.md'), stream.pipe((0, _capitalGainsCalculateStream.default)({
})).pipe((0, _tradeTransactionsStream.default)()).pipe((0, _capitalGainsCalculateStream.default)({
assets,
forwardByAsset

@@ -162,0 +173,0 @@ })).pipe((0, _capitalGainsFormatStream.default)()), _fs.default.createReadStream(__dirname + '/../res/output_footer.md')]); // Convert the output from Markdown to HTML.

@@ -204,2 +204,38 @@ 'use strict';

/**
* Transforms a CSV record from KuCoin into a trade.
* @param {object} chunk The CSV record.
*/
async _transformKuCoin(chunk) {
// If this is not a trade, then drop it.
let buySell = chunk['Buy/Sell'];
if (buySell !== 'Buy' && buySell !== 'Sell') return;
let [quoteAsset, baseAsset] = chunk.Coin.split('/');
baseAsset = _assets.default.normalizeCode(baseAsset);
quoteAsset = _assets.default.normalizeCode(quoteAsset);
const splitAmountAssetRegExp = /^([0-9.,]+)([A-Za-z][A-Za-z0-9]*)$/;
let [amount, amountAsset] = chunk.Amount.match(splitAmountAssetRegExp).slice(1);
let [feeAmount, feeAsset] = chunk.Fee.match(splitAmountAssetRegExp).slice(1);
amountAsset = _assets.default.normalizeCode(amountAsset);
feeAsset = _assets.default.normalizeCode(feeAsset);
if (amountAsset !== quoteAsset) {
console.log('WARNING: Expected amount of ' + quoteAsset + ' but found ' + amountAsset + ' instead.');
return;
}
this.push({
exchange: 'KuCoin',
baseAsset: baseAsset,
quoteAsset: quoteAsset,
baseAmount: TradeParseStream._parseNumber(chunk['Filled Price']),
quoteAmount: TradeParseStream._parseNumber(chunk.Amount),
sell: buySell === 'Sell',
time: TradeParseStream._parseTime(chunk.Time),
feeAsset: baseAsset,
feeAmount: feeAmount
});
}
/**
* Parses a number.

@@ -231,3 +267,4 @@ * @param {string} s The string.

'Uuid|Exchange|TimeStamp|OrderType|Limit|Quantity|QuantityRemaining|Commission|Price|PricePerUnit|IsConditional|Condition|ConditionTarget|ImmediateOrCancel|Closed': TradeParseStream.prototype._transformBittrex2,
'txid|refid|time|type|aclass|asset|amount|fee|balance': TradeParseStream.prototype._transformKraken
'txid|refid|time|type|aclass|asset|amount|fee|balance': TradeParseStream.prototype._transformKraken,
'Coin|Time|Buy/Sell|Filled Price|Amount|Fee|Volume': TradeParseStream.prototype._transformKuCoin
/**

@@ -234,0 +271,0 @@ * Initializes a new instance.

@@ -17,10 +17,10 @@ 'use strict';

* @typedef {object} Transaction
* @property {string} exchange The exchange on which the transaction was executed.
* @property {string} asset The asset.
* @property {number} amount The amount of assets.
* @property {number} value The value of the transaction, in Canadian dollars.
* @property {number} time The time of the transaction, as a UNIX timestamp.
* @property {string} feeAsset The currency of the transaction fee.
* @property {number} feeAmount The amount of the transaction fee.
* @property {number} feeValue The value of the transaction fee, in Canadian dollars.
* @property {string} exchange The exchange on which the transaction was executed.
* @property {string} asset The asset.
* @property {number} amount The amount of assets.
* @property {number} value The value of the transaction, in Canadian dollars.
* @property {number} time The time of the transaction, as a UNIX timestamp.
* @property {string} [feeAsset] The asset of the transaction fee.
* @property {number} [feeAmount] The amount of the transaction fee.
* @property {number} feeValue The value of the transaction fee, in Canadian dollars.
*/

@@ -52,4 +52,2 @@

time: chunk.time,
feeAsset: chunk.baseAsset,
feeAmount: 0,
feeValue: 0

@@ -63,4 +61,2 @@ };

time: chunk.time,
feeAsset: chunk.quoteAsset,
feeAmount: 0,
feeValue: 0

@@ -71,22 +67,24 @@ };

if (chunk.sell) {
quoteChunk.amount = -quoteChunk.amount;
chunks.reverse();
} else {
baseChunk.amount = -baseChunk.amount;
} // Copy the fee.
}
chunks[0].amount = -chunks[0].amount; // Drop the chunks that are empty.
if (chunk.sell) {
quoteChunk.feeAsset = chunk.feeAsset;
quoteChunk.feeAmount = chunk.feeAmount;
quoteChunk.feeValue = chunk.feeValue;
} else {
baseChunk.feeAsset = chunk.feeAsset;
baseChunk.feeAmount = chunk.feeAmount;
baseChunk.feeValue = chunk.feeValue;
} // Drop the chunks that represent fiat currencies.
chunks = chunks.filter(function (chunk) {
return chunk.amount;
}); // Drop the chunks that represent fiat currencies.
for (let i = 0; i < chunks.length; ++i) if (_assets.default.getPriority(chunks[i].asset) === 0) chunks.splice(i--, 1);
if (!chunks.length) {
callback();
return;
} // Set the transaction fee of the disposed asset.
chunks[0].feeValue = chunk.feeValue; // Set the transaction fee of the acquired asset.
chunks[chunks.length - 1].feeAsset = chunk.feeAsset;
chunks[chunks.length - 1].feeAmount = chunk.feeAmount;
for (let chunk of chunks) this.push(chunk);

@@ -93,0 +91,0 @@

{
"name": "@davidosborn/crypto-tax-calculator",
"version": "0.0.14",
"version": "0.0.15",
"description": "A tool to calculate the capital gains of cryptocurrency assets for Canadian taxes",

@@ -50,2 +50,3 @@ "keywords": [

"fromentries": "*",
"line-stream": "*",
"markdown-table": "*",

@@ -52,0 +53,0 @@ "marked": "*",

@@ -26,3 +26,3 @@ 'use strict'

* @typedef {object} CapitalGains
* @property {Map.<string, Forward>} forwardByAsset The assets that were carried forward from last year.
* @property {Map.<string, Forward>} [forwardByAsset] The assets that were carried forward from last year.
* @property {array.<Trade>} trades The trades.

@@ -46,4 +46,5 @@ * @property {Map.<string, Ledger>} ledgerByAsset The ledger of each asset.

* Initializes a new instance.
* @param {object} [options] The options.
* @param {object.<string, Forward>} [options.forwardByAsset] The assets to carry forward from last year.
* @param {object} [options] The options.
* @param {Set.<string>} [options.assets] The assets to retain.
* @param {Map.<string, Forward>} [options.forwardByAsset] The assets to carry forward from last year.
*/

@@ -59,7 +60,5 @@ constructor(options) {

* The assets to carry forward from last year.
* @type {Map.<string, Ledger>}
* @type {Map.<string, Forward>}
*/
this._forwardByAsset = new Map(this._options?.forwardByAsset
? Object.entries(this._options.forwardByAsset)
: [])
this._forwardByAsset = this._options?.forwardByAsset

@@ -85,4 +84,4 @@ /**

// Initialize the ledger of the assets to carry forward from last year.
if (this._forwardByAsset) {
for (let [asset, forward] of this._forwardByAsset) {
if (this._forwardByAsset)
for (let [asset, forward] of this._forwardByAsset.entries())
this._ledgerByAsset.set(asset, {

@@ -93,4 +92,2 @@ acb: forward.acb,

})
}
}
}

@@ -116,5 +113,4 @@

if (chunk.amount < 0) {
if (!ledger.balance) {
if (!ledger.balance)
console.log('WARNING: Disposition of ' + chunk.asset + ' from an empty balance on ' + formatTime(chunk.time) + '.')
}

@@ -142,3 +138,3 @@ let acbPerUnit = ledger.balance ? ledger.acb / ledger.balance : 0

// Remove the transaction fee from the balance.
// Remove the transaction fee from the balance, except for fiat currencies.
if (chunk.feeAmount) {

@@ -153,4 +149,5 @@ let feeLedger = this._ledgerByAsset.get(chunk.feeAsset)

// Record the balance in the trade.
// Record the balance and ACB in the trade for information purposes.
chunk.balance = ledger.balance
chunk.acb = ledger.acb

@@ -160,5 +157,9 @@ // Check whether the balance is negative, which would indicate an accounting error.

this._assetsWithNegativeBalance.add(chunk.asset)
console.log('WARNING: Encountered a negative balance for ' + chunk.asset + '.')
console.log('WARNING: Encountered a negative balance for ' + chunk.asset + ' on ' + formatTime(chunk.time) + '.')
}
// Clear the ACB when the balance is negative.
if (ledger.balance <= 0)
ledger.acb = 0
callback()

@@ -213,3 +214,3 @@ }

ledgerByAsset: this._ledgerByAsset,
aggregateDisposition: aggregateDisposition,
aggregateDisposition,
taxableGain: aggregateDisposition.gain / 2 // Capital gains are taxable at 50%.

@@ -216,0 +217,0 @@ })

'use strict'
import markdownTable from 'markdown-table'
import marked from 'marked'
import stream from 'stream'
import Assets from './assets'
import formatTime from './format-time'

@@ -47,3 +48,3 @@ /**

// Write the balance that was carried forward from last year.
if (chunk.forwardByAsset.size) {
if (chunk.forwardByAsset?.size) {
this._pushLine()

@@ -72,8 +73,9 @@ this._pushLine('## Carried forward from last year')

'Asset',
'Units acquired',
'Units acquired (or disposed)',
'Value',
'Balance',
'Adjusted cost base',
'Fee',
'Fee asset',
'Date',
'Time',
'Exchange'

@@ -86,5 +88,6 @@ ]]

this._formatAmount(trade.balance),
this._formatAmount(trade.feeAmount),
trade.feeAsset,
this._formatDate(trade.time),
this._formatValue(trade.acb),
trade.feeAsset ? this._formatAssetAmount(trade.feeAsset, trade.feeAmount) : '',
trade.feeAsset ? trade.feeAsset : '',
formatTime(trade.time),
trade.exchange

@@ -111,3 +114,3 @@ ]))))

'Gain (or loss)',
'Date',
'Time',
'Exchange'

@@ -123,3 +126,3 @@ ]]

this._formatValue(disposition.gain),
this._formatDate(disposition.time),
formatTime(disposition.time),
disposition.exchange

@@ -215,2 +218,8 @@ ])

_formatAssetAmount(asset, amount) {
return Assets.getPriority(asset) !== 0
? this._formatAmount(amount)
: this._formatValue(amount)
}
_formatAmount(amount) {

@@ -230,14 +239,2 @@ let amountString = this._amountFormat.format(Math.abs(amount))

_formatDate(time) {
return new Date(time)
.toLocaleDateString('en-CA', {
day: 'numeric',
hour: '2-digit',
minute: '2-digit',
month: 'short',
second: '2-digit',
year: 'numeric'
})
}
_pushLine(line) {

@@ -244,0 +241,0 @@ this.push(line)

@@ -15,3 +15,3 @@ 'use strict'

minute: '2-digit',
month: '2-digit',
month: 'short',
year: 'numeric'

@@ -18,0 +18,0 @@ })

@@ -8,2 +8,3 @@ 'use strict'

import fs from 'fs'
import lineStream from 'line-stream'
import multiStream from 'multistream'

@@ -19,6 +20,7 @@ import process from 'process'

import markedStream from './marked-stream'
import csvNormalizeStream from './csv-normalize-stream'
import tradeFilterStream from './trade-filter-stream'
import tradeParseStream from './trade-parse-stream'
import tradeTransactionsStream from './trade-transactions-stream'
import tradeValueStream from './trade-value-stream'
import transactionFilterStream from './transaction-filter-stream'

@@ -33,3 +35,3 @@ export default function main(args) {

argument: 'spec',
description: 'Only consider the specified assets.'
description: 'Only consider trades involving the specified assets.'
},

@@ -51,3 +53,3 @@ {

long: 'html',
description: 'Format the output as HTML instead of Markdown.'
description: 'Format the results as HTML instead of Markdown.'
},

@@ -58,3 +60,3 @@ {

argument: 'file',
description: 'Write the output to the specified file.'
description: 'Write the results to the specified file.'
},

@@ -64,3 +66,3 @@ {

long: 'quiet',
description: 'Do not produce any output.'
description: 'Do not write the results.'
},

@@ -110,6 +112,11 @@ {

// Parse the assets to retain when filtering the trades.
let assets = undefined
if (opts.options.assets)
assets = new Set(opts.options.assets.value.split(',').map(Assets.normalizeCode))
// Parse the initial balance and ACB of each asset to carry it forward from last year.
let forwardByAsset = null
if (opts.options.init)
forwardByAsset = fromEntries(opts.options.init.value.split(',')
let forwardByAsset = undefined
if (opts.options.init) {
forwardByAsset = new Map(opts.options.init.value.split(',')
.map(function(spec) {

@@ -122,2 +129,3 @@ let [asset, balance, acb] = spec.split(':')

}))
}

@@ -134,2 +142,4 @@ // Load the historical data.

.pipe(utf8())
.pipe(lineStream())
.pipe(csvNormalizeStream())
.pipe(csvParse({

@@ -147,16 +157,8 @@ columns: true,

stream = stream
.pipe(tradeValueStream({
history,
verbose: !!opts.options.verbose,
web: !!opts.options.web
// Filter the trades by their assets.
if (assets)
stream = stream.pipe(tradeFilterStream({
assets
}))
.pipe(tradeTransactionsStream())
// Limit the assets.
if (opts.options.assets)
stream = stream.pipe(transactionFilterStream({
assets: new Set(opts.options.assets.value.split(',').map(Assets.normalizeCode))
}))
// Calculate the capital gains.

@@ -166,3 +168,10 @@ stream = multiStream([

stream
.pipe(tradeValueStream({
history,
verbose: !!opts.options.verbose,
web: !!opts.options.web
}))
.pipe(tradeTransactionsStream())
.pipe(capitalGainsCalculateStream({
assets,
forwardByAsset

@@ -169,0 +178,0 @@ }))

@@ -36,3 +36,4 @@ 'use strict'

'Uuid|Exchange|TimeStamp|OrderType|Limit|Quantity|QuantityRemaining|Commission|Price|PricePerUnit|IsConditional|Condition|ConditionTarget|ImmediateOrCancel|Closed': TradeParseStream.prototype._transformBittrex2,
'txid|refid|time|type|aclass|asset|amount|fee|balance': TradeParseStream.prototype._transformKraken
'txid|refid|time|type|aclass|asset|amount|fee|balance': TradeParseStream.prototype._transformKraken,
'Coin|Time|Buy/Sell|Filled Price|Amount|Fee|Volume': TradeParseStream.prototype._transformKuCoin
}

@@ -205,2 +206,42 @@

/**
* Transforms a CSV record from KuCoin into a trade.
* @param {object} chunk The CSV record.
*/
async _transformKuCoin(chunk) {
// If this is not a trade, then drop it.
let buySell = chunk['Buy/Sell']
if (buySell !== 'Buy' && buySell !== 'Sell')
return
let [quoteAsset,baseAsset] = chunk.Coin.split('/')
baseAsset = Assets.normalizeCode(baseAsset)
quoteAsset = Assets.normalizeCode(quoteAsset)
const splitAmountAssetRegExp = /^([0-9.,]+)([A-Za-z][A-Za-z0-9]*)$/
let [amount, amountAsset] = chunk.Amount.match(splitAmountAssetRegExp).slice(1)
let [feeAmount, feeAsset] = chunk.Fee.match(splitAmountAssetRegExp).slice(1)
amountAsset = Assets.normalizeCode(amountAsset)
feeAsset = Assets.normalizeCode(feeAsset)
if (amountAsset !== quoteAsset) {
console.log('WARNING: Expected amount of ' + quoteAsset + ' but found ' + amountAsset + ' instead.')
return
}
this.push({
exchange: 'KuCoin',
baseAsset: baseAsset,
quoteAsset: quoteAsset,
baseAmount: TradeParseStream._parseNumber(chunk['Filled Price']),
quoteAmount: TradeParseStream._parseNumber(chunk.Amount),
sell: buySell === 'Sell',
time: TradeParseStream._parseTime(chunk.Time),
feeAsset: baseAsset,
feeAmount: feeAmount
})
}
/**
* Parses a number.

@@ -207,0 +248,0 @@ * @param {string} s The string.

@@ -9,10 +9,10 @@ 'use strict'

* @typedef {object} Transaction
* @property {string} exchange The exchange on which the transaction was executed.
* @property {string} asset The asset.
* @property {number} amount The amount of assets.
* @property {number} value The value of the transaction, in Canadian dollars.
* @property {number} time The time of the transaction, as a UNIX timestamp.
* @property {string} feeAsset The currency of the transaction fee.
* @property {number} feeAmount The amount of the transaction fee.
* @property {number} feeValue The value of the transaction fee, in Canadian dollars.
* @property {string} exchange The exchange on which the transaction was executed.
* @property {string} asset The asset.
* @property {number} amount The amount of assets.
* @property {number} value The value of the transaction, in Canadian dollars.
* @property {number} time The time of the transaction, as a UNIX timestamp.
* @property {string} [feeAsset] The asset of the transaction fee.
* @property {number} [feeAmount] The amount of the transaction fee.
* @property {number} feeValue The value of the transaction fee, in Canadian dollars.
*/

@@ -43,4 +43,2 @@

time: chunk.time,
feeAsset: chunk.baseAsset,
feeAmount: 0,
feeValue: 0

@@ -54,4 +52,2 @@ }

time: chunk.time,
feeAsset: chunk.quoteAsset,
feeAmount: 0,
feeValue: 0

@@ -63,21 +59,12 @@ }

if (chunk.sell) {
quoteChunk.amount = -quoteChunk.amount
chunks.reverse()
}
else {
baseChunk.amount = -baseChunk.amount
}
// Copy the fee.
if (chunk.sell) {
quoteChunk.feeAsset = chunk.feeAsset
quoteChunk.feeAmount = chunk.feeAmount
quoteChunk.feeValue = chunk.feeValue
}
else {
baseChunk.feeAsset = chunk.feeAsset
baseChunk.feeAmount = chunk.feeAmount
baseChunk.feeValue = chunk.feeValue
}
chunks[0].amount = -chunks[0].amount
// Drop the chunks that are empty.
chunks = chunks.filter(function(chunk) {
return chunk.amount
})
// Drop the chunks that represent fiat currencies.

@@ -88,2 +75,14 @@ for (let i = 0; i < chunks.length; ++i)

if (!chunks.length) {
callback()
return
}
// Set the transaction fee of the disposed asset.
chunks[0].feeValue = chunk.feeValue
// Set the transaction fee of the acquired asset.
chunks[chunks.length - 1].feeAsset = chunk.feeAsset
chunks[chunks.length - 1].feeAmount = chunk.feeAmount
for (let chunk of chunks)

@@ -90,0 +89,0 @@ this.push(chunk)

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc