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.13 to 0.0.14

lib/trade-transactions-stream.js

52

lib/capital-gains-calculate-stream.js

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

* @typedef {object} CapitalGains
* @property {array.<Trade>} trades The trades.
* @property {object.<string, Ledger>} ledgerByAsset The ledger of each asset.
* @property {Disposition} aggregateDisposition The aggregate disposition.
* @property {number} taxableGain The taxable gain (or loss).
* @property {Map.<string, Forward>} forwardByAsset The assets that were carried forward from last year.
* @property {array.<Trade>} trades The trades.
* @property {Map.<string, Ledger>} ledgerByAsset The ledger of each asset.
* @property {Disposition} aggregateDisposition The aggregate disposition.
* @property {number} taxableGain The taxable gain (or loss).
*/
/**
* The initial balance and ACB of an asset.
* This is typically carried forward from the previous year.
* The initial balance and ACB of an asset that was carried forward from last year.
* @typedef {object} Forward

@@ -57,10 +57,19 @@ * @property {number} balance The balance.

* Initializes a new instance.
* @param {object} [options] The options.
* @param {object.<string, Forward>} [options.forward] The initial balance and ACB of each asset.
* @param {object} [options] The options.
* @param {object.<string, Forward>} [options.forwardByAsset] The assets to carry forward from last year.
*/
constructor(options) {
var _this$_options;
super({
objectMode: true
});
this._options = options;
/**
* The assets to carry forward from last year.
* @type {Map.<string, Ledger>}
*/
this._forwardByAsset = new Map(((_this$_options = this._options) === null || _this$_options === void 0 ? void 0 : _this$_options.forwardByAsset) ? Object.entries(this._options.forwardByAsset) : []);
/**
* The trades.

@@ -82,6 +91,6 @@ * @type {Trade}

this._assetsWithNegativeBalance = new Set(); // Create the ledger for assets that have been carried forward from the previous year.
this._assetsWithNegativeBalance = new Set(); // Initialize the ledger of the assets to carry forward from last year.
if (options === null || options === void 0 ? void 0 : options.forward) {
for (let [asset, forward] of Object.entries(options.forward)) {
if (this._forwardByAsset) {
for (let [asset, forward] of this._forwardByAsset) {
this._ledgerByAsset.set(asset, {

@@ -124,3 +133,3 @@ acb: forward.acb,

pod: chunk.value,
oae: chunk.fee,
oae: chunk.feeValue,
time: chunk.time

@@ -132,6 +141,20 @@ };

ledger.acb += acbPerUnit * chunk.amount;
} else ledger.acb += chunk.value + chunk.fee;
} else ledger.acb += chunk.value + chunk.feeValue; // Update the balance.
ledger.balance += chunk.amount; // Check whether the balance is negative, which would indicate an accounting error.
ledger.balance += chunk.amount; // Remove the transaction fee from the balance.
if (chunk.feeAmount) {
let feeLedger = this._ledgerByAsset.get(chunk.feeAsset);
if (feeLedger !== undefined) {
let feeAcbPerUnit = feeLedger.balance ? feeLedger.acb / feeLedger.balance : 0;
feeLedger.acb -= feeAcbPerUnit * chunk.feeAmount;
feeLedger.balance -= chunk.feeAmount;
}
} // Record the balance in the trade.
chunk.balance = ledger.balance; // Check whether the balance is negative, which would indicate an accounting error.
if (ledger.balance < -0.000000005 && !this._assetsWithNegativeBalance.has(chunk.asset)) {

@@ -183,2 +206,3 @@ this._assetsWithNegativeBalance.add(chunk.asset);

this.push({
forwardByAsset: this._forwardByAsset,
trades: this._trades,

@@ -185,0 +209,0 @@ ledgerByAsset: this._ledgerByAsset,

@@ -52,3 +52,14 @@ 'use strict';

_transform(chunk, encoding, callback) {
// Write the trades.
// Write the balance that was carried forward from last year.
if (chunk.forwardByAsset.size) {
this._pushLine();
this._pushLine('## Carried forward from last year');
this._pushLine();
this._pushLine((0, _markdownTable.default)([['Asset', 'Balance', 'Adjusted cost base']].concat(Array.from(chunk.forwardByAsset, ([asset, forward]) => [asset, this._formatAmount(forward.balance), this._formatValue(forward.acb)]))));
} // Write the trades.
this._pushLine();

@@ -60,3 +71,3 @@

this._pushLine((0, _markdownTable.default)([['Asset', 'Units acquired (or disposed)', 'Value', 'Fee', 'Date', 'Exchange']].concat(Array.from(chunk.trades, trade => [trade.asset, this._formatAmount(trade.amount), this._formatValue(trade.value), this._formatValue(trade.fee), this._formatDate(trade.time), trade.exchange])))); // Sort the assets.
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.

@@ -75,5 +86,7 @@

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]))))); // Write the aggregate disposition per asset.
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.
let ledgerByAssetWithDisposition = ledgerByAsset.filter(([asset, ledger]) => ledger.dispositions.length); // Write the aggregate disposition per asset.
this._pushLine();

@@ -85,3 +98,3 @@

this._pushLine((0, _markdownTable.default)([['Asset', 'Units', 'Proceeds of disposition', 'Adjusted cost base', 'Outlays and expenses', 'Gain (or loss)']].concat(ledgerByAsset.map(([asset, ledger]) => [asset, this._formatAmount(ledger.aggregateDisposition.amount), this._formatValue(ledger.aggregateDisposition.pod), this._formatValue(ledger.aggregateDisposition.acb), this._formatValue(ledger.aggregateDisposition.oae), this._formatValue(ledger.aggregateDisposition.gain)])))); // Write the summary per asset.
this._pushLine((0, _markdownTable.default)([['Asset', 'Units', 'Proceeds of disposition', 'Adjusted cost base', 'Outlays and expenses', 'Gain (or loss)']].concat(ledgerByAssetWithDisposition.map(([asset, ledger]) => [asset, this._formatAmount(ledger.aggregateDisposition.amount), this._formatValue(ledger.aggregateDisposition.pod), this._formatValue(ledger.aggregateDisposition.acb), this._formatValue(ledger.aggregateDisposition.oae), this._formatValue(ledger.aggregateDisposition.gain)])))); // Write the summary per asset.

@@ -112,7 +125,7 @@

this._pushLine('## Carry forward');
this._pushLine('## Carry forward to the next year');
this._pushLine();
this._pushLine('The following specification can be passed to the calculator next year to carry forward the previous year\'s balance and adjusted cost base.');
this._pushLine('The following specification can be passed to the calculator next year to carry forward this year\'s balance and adjusted cost base.');

@@ -123,3 +136,3 @@ this._pushLine();

this._pushLine('--forward=\\');
this._pushLine('--init=\\');

@@ -161,3 +174,6 @@ for (let [asset, ledger] of ledgerByAssetWithBalance) {

day: 'numeric',
hour: '2-digit',
minute: '2-digit',
month: 'short',
second: '2-digit',
year: 'numeric'

@@ -164,0 +180,0 @@ });

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

var _assets = _interopRequireDefault(require("./assets"));
var _capitalGainsCalculateStream = _interopRequireDefault(require("./capital-gains-calculate-stream"));

@@ -39,6 +41,8 @@

var _tradeSeparateStream = _interopRequireDefault(require("./trade-separate-stream"));
var _tradeTransactionsStream = _interopRequireDefault(require("./trade-transactions-stream"));
var _tradeValueStream = _interopRequireDefault(require("./trade-value-stream"));
var _transactionFilterStream = _interopRequireDefault(require("./transaction-filter-stream"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -54,6 +58,6 @@

options: [{
short: 'f',
long: 'forward',
short: 'a',
long: ['assets', 'asset'],
argument: 'spec',
description: 'Define the initial balance and ACB of the assets.'
description: 'Only consider the specified assets.'
}, {

@@ -65,2 +69,7 @@ short: 'h',

}, {
short: 'i',
long: 'init',
argument: 'spec',
description: 'Define the initial balance and ACB of the assets.'
}, {
short: 'm',

@@ -98,4 +107,4 @@ long: 'html',

usage: {
footer: _fs.default.readFileSync(__dirname + '/../res/cmdline_footer.txt', 'utf8').replace(/([.,;:])\r?\n/g, '$1 '),
header: _fs.default.readFileSync(__dirname + '/../res/cmdline_header.txt', 'utf8').replace(/([.,;:])\r?\n/g, '$1 '),
footer: _fs.default.readFileSync(__dirname + '/../res/cmdline_footer.txt', 'utf8').replace(/([.,;:])\r?\n([A-Za-z])/g, '$1 $2'),
header: _fs.default.readFileSync(__dirname + '/../res/cmdline_header.txt', 'utf8').replace(/([.,;:])\r?\n([A-Za-z])/g, '$1 $2'),
program: 'crypto-tax-calculator',

@@ -114,6 +123,6 @@ spec: '[option]... <csv-file>...'

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.
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.
let forward = null;
if (opts.options.forward) forward = (0, _fromentries.default)(opts.options.forward.value.split(',').map(function (spec) {
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(':');

@@ -137,10 +146,15 @@ return [asset, {

if (opts.options.take) stream = stream.pipe((0, _takeStream.default)(parseInt(opts.options.take.value))); // Continue creating the stream.
stream = (0, _multistream.default)([_fs.default.createReadStream(__dirname + '/../res/output_header.md'), stream.pipe((0, _tradeValueStream.default)({
if (opts.options.take) stream = stream.pipe((0, _takeStream.default)(parseInt(opts.options.take.value)));
stream = stream.pipe((0, _tradeValueStream.default)({
history,
verbose: !!opts.options.verbose,
web: !!opts.options.web
})).pipe((0, _tradeSeparateStream.default)()).pipe((0, _capitalGainsCalculateStream.default)({
forward
})).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)({
forwardByAsset
})).pipe((0, _capitalGainsFormatStream.default)()), _fs.default.createReadStream(__dirname + '/../res/output_footer.md')]); // Convert the output from Markdown to HTML.

@@ -147,0 +161,0 @@

@@ -28,8 +28,8 @@ 'use strict';

* @property {number} quoteAmount The amount of the quote currency.
* @property {number} [value] The value of the assets, in Canadian dollars.
* @property {boolean} sell True if the trade represents a sale.
* @property {number} time The time at which the trade occurred, as a UNIX timestamp.
* @property {string} feeAsset The currency of the trading fee.
* @property {number} feeAmount The amount of the trading fee.
* @property {number} [value] The value of the assets, in Canadian dollars.
* @property {number} [fee] The value of the trading fee, in Canadian dollars.
* @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.
*/

@@ -137,2 +137,7 @@

quoteAsset = _assets.default.normalizeCode(quoteAsset);
let quantity = TradeParseStream._parseNumber(chunk.Quantity);
let quantityRemaining = TradeParseStream._parseNumber(chunk.QuantityRemaining);
this.push({

@@ -143,3 +148,3 @@ exchange: 'Bittrex',

baseAmount: TradeParseStream._parseNumber(chunk.Price),
quoteAmount: TradeParseStream._parseNumber(chunk.Quantity),
quoteAmount: quantity - quantityRemaining,
sell: chunk.OrderType.includes('SELL'),

@@ -158,38 +163,42 @@ time: TradeParseStream._parseTime(chunk.TimeStamp),

async _transformKraken(chunk) {
let chunks = this._tradeChunks;
let chunks = this._tradeChunks; // We only care about trades.
if (chunk.type === 'trade') {
// Normalize the properties of the chunk.
chunk = {
asset: _assets.default.normalizeCode(chunk.asset),
amount: TradeParseStream._parseNumber(chunk.amount),
time: TradeParseStream._parseTime(chunk.time),
fee: TradeParseStream._parseNumber(chunk.fee) // Process two consecutive trade chunks as a single trade.
if (chunk.type !== 'trade') {
if (chunks.length > 0) {
console.log('WARNING: Found unpaired trade chunk.');
chunks.length = 0;
}
};
chunks.push(chunk);
return;
} // Normalize the properties of the chunk.
if (chunks.length === 2) {
// Ensure the chunks have the same timestamp.
if (chunks[0].time !== chunks[1].time) console.log('WARNING: Found paired trade chunks with different timestamps.'); // Determine which chunks represent the base and quote of the currency pair.
let priorities = chunks.map(c => _assets.default.getPriority(c.asset));
let isCurrencyPairReversed = priorities[0] < priorities[1];
let baseChunk = chunks[+!isCurrencyPairReversed];
let quoteChunk = chunks[+isCurrencyPairReversed];
this.push({
exchange: 'Kraken',
baseAsset: baseChunk.asset,
quoteAsset: quoteChunk.asset,
baseAmount: Math.abs(baseChunk.amount),
quoteAmount: Math.abs(quoteChunk.amount),
sell: baseChunk.amount > 0,
time: baseChunk.time,
feeAsset: baseChunk.asset,
feeAmount: baseChunk.fee
});
chunks.length = 0;
}
} else if (chunks.length > 0) {
console.log('WARNING: Found unpaired trade chunk.');
chunk = {
asset: _assets.default.normalizeCode(chunk.asset),
amount: TradeParseStream._parseNumber(chunk.amount),
time: TradeParseStream._parseTime(chunk.time),
fee: TradeParseStream._parseNumber(chunk.fee) // Process two consecutive trade chunks as a single trade.
};
chunks.push(chunk);
if (chunks.length === 2) {
// Ensure the chunks have the same timestamp.
if (chunks[0].time !== chunks[1].time) console.log('WARNING: Found paired trade chunks with different timestamps.'); // Determine which chunks represent the base and quote of the currency pair.
let priorities = chunks.map(c => _assets.default.getPriority(c.asset));
let isCurrencyPairReversed = priorities[0] < priorities[1];
let baseChunk = chunks[+!isCurrencyPairReversed];
let quoteChunk = chunks[+isCurrencyPairReversed];
this.push({
exchange: 'Kraken',
baseAsset: baseChunk.asset,
quoteAsset: quoteChunk.asset,
baseAmount: Math.abs(baseChunk.amount),
quoteAmount: Math.abs(quoteChunk.amount),
sell: baseChunk.amount > 0,
time: baseChunk.time,
feeAsset: baseChunk.asset,
feeAmount: baseChunk.fee
});
chunks.length = 0;

@@ -196,0 +205,0 @@ }

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

chunk.fee = chunk.feeAsset === chunk.baseAsset ? chunk.value * chunk.feeAmount / chunk.baseAmount : chunk.feeAsset === chunk.quoteAsset ? chunk.value * chunk.feeAmount / chunk.quoteAmount : await this._getValue(chunk.feeAsset, chunk.feeAmount, chunk.time);
chunk.feeValue = chunk.feeAsset === chunk.baseAsset ? chunk.value * chunk.feeAmount / chunk.baseAmount : chunk.feeAsset === chunk.quoteAsset ? chunk.value * chunk.feeAmount / chunk.quoteAmount : await this._getValue(chunk.feeAsset, chunk.feeAmount, chunk.time);
this.push(chunk);

@@ -61,0 +61,0 @@ callback();

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

@@ -5,0 +5,0 @@ "keywords": [

The initial balance of the assets can be defined by the '--balance' option.
The argument is a comma-separated sequence of assets and their balances,
in the form of '<asset>:<balance>,...', such as '--balance=BTC:1,ETH:5,LTC:10'.
The calculation can be limited to a subset of the available assets by the '--assets' option.
The argument is a comma-separated list of assets.
The balances can be carried forward from last year by the '--init' option.
The argument is a comma-separated list of assets, each with its balance and adjusted cost base,
in the form of '<asset>:<balance>:<acb>,...', such as '--init=BTC:1:5000,ETH:5:1000,LTC:10:80'.

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

* @typedef {object} CapitalGains
* @property {array.<Trade>} trades The trades.
* @property {object.<string, Ledger>} ledgerByAsset The ledger of each asset.
* @property {Disposition} aggregateDisposition The aggregate disposition.
* @property {number} taxableGain The taxable gain (or loss).
* @property {Map.<string, Forward>} forwardByAsset The assets that were carried forward from last year.
* @property {array.<Trade>} trades The trades.
* @property {Map.<string, Ledger>} ledgerByAsset The ledger of each asset.
* @property {Disposition} aggregateDisposition The aggregate disposition.
* @property {number} taxableGain The taxable gain (or loss).
*/
/**
* The initial balance and ACB of an asset.
* This is typically carried forward from the previous year.
* The initial balance and ACB of an asset that was carried forward from last year.
* @typedef {object} Forward

@@ -46,4 +46,4 @@ * @property {number} balance The balance.

* Initializes a new instance.
* @param {object} [options] The options.
* @param {object.<string, Forward>} [options.forward] The initial balance and ACB of each asset.
* @param {object} [options] The options.
* @param {object.<string, Forward>} [options.forwardByAsset] The assets to carry forward from last year.
*/

@@ -55,3 +55,13 @@ constructor(options) {

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

@@ -74,5 +84,5 @@ * @type {Trade}

// Create the ledger for assets that have been carried forward from the previous year.
if (options?.forward) {
for (let [asset, forward] of Object.entries(options.forward)) {
// Initialize the ledger of the assets to carry forward from last year.
if (this._forwardByAsset) {
for (let [asset, forward] of this._forwardByAsset) {
this._ledgerByAsset.set(asset, {

@@ -115,3 +125,3 @@ acb: forward.acb,

pod: chunk.value,
oae: chunk.fee,
oae: chunk.feeValue,
time: chunk.time

@@ -126,6 +136,20 @@ }

else
ledger.acb += chunk.value + chunk.fee
ledger.acb += chunk.value + chunk.feeValue
// Update the balance.
ledger.balance += chunk.amount
// Remove the transaction fee from the balance.
if (chunk.feeAmount) {
let feeLedger = this._ledgerByAsset.get(chunk.feeAsset)
if (feeLedger !== undefined) {
let feeAcbPerUnit = feeLedger.balance ? feeLedger.acb / feeLedger.balance : 0
feeLedger.acb -= feeAcbPerUnit * chunk.feeAmount
feeLedger.balance -= chunk.feeAmount
}
}
// Record the balance in the trade.
chunk.balance = ledger.balance
// Check whether the balance is negative, which would indicate an accounting error.

@@ -183,2 +207,3 @@ if (ledger.balance < -0.000000005 && !this._assetsWithNegativeBalance.has(chunk.asset)) {

this.push({
forwardByAsset: this._forwardByAsset,
trades: this._trades,

@@ -185,0 +210,0 @@ ledgerByAsset: this._ledgerByAsset,

@@ -46,2 +46,20 @@ 'use strict'

_transform(chunk, encoding, callback) {
// Write the balance that was carried forward from last year.
if (chunk.forwardByAsset.size) {
this._pushLine()
this._pushLine('## Carried forward from last year')
this._pushLine()
this._pushLine(markdownTable(
[[
'Asset',
'Balance',
'Adjusted cost base'
]]
.concat(Array.from(chunk.forwardByAsset, ([asset, forward]) => [
asset,
this._formatAmount(forward.balance),
this._formatValue(forward.acb)
]))))
}
// Write the trades.

@@ -54,5 +72,7 @@ this._pushLine()

'Asset',
'Units acquired (or disposed)',
'Units acquired',
'Value',
'Balance',
'Fee',
'Fee asset',
'Date',

@@ -65,3 +85,5 @@ 'Exchange'

this._formatValue(trade.value),
this._formatValue(trade.fee),
this._formatAmount(trade.balance),
this._formatAmount(trade.feeAmount),
trade.feeAsset,
this._formatDate(trade.time),

@@ -105,2 +127,6 @@ trade.exchange

// Find the assets with dispositions.
let ledgerByAssetWithDisposition = ledgerByAsset
.filter(([asset, ledger]) => ledger.dispositions.length)
// Write the aggregate disposition per asset.

@@ -119,3 +145,3 @@ this._pushLine()

]]
.concat(ledgerByAsset.map(([asset, ledger]) => [
.concat(ledgerByAssetWithDisposition.map(([asset, ledger]) => [
asset,

@@ -169,8 +195,8 @@ this._formatAmount(ledger.aggregateDisposition.amount),

this._pushLine()
this._pushLine('## Carry forward')
this._pushLine('## Carry forward to the next year')
this._pushLine()
this._pushLine('The following specification can be passed to the calculator next year to carry forward the previous year\'s balance and adjusted cost base.')
this._pushLine('The following specification can be passed to the calculator next year to carry forward this year\'s balance and adjusted cost base.')
this._pushLine()
this._pushLine('```')
this._pushLine('--forward=\\')
this._pushLine('--init=\\')
for (let [asset, ledger] of ledgerByAssetWithBalance) {

@@ -208,3 +234,6 @@ let last = asset === ledgerByAssetWithBalance[ledgerByAssetWithBalance.length - 1][0]

day: 'numeric',
hour: '2-digit',
minute: '2-digit',
month: 'short',
second: '2-digit',
year: 'numeric'

@@ -211,0 +240,0 @@ })

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

import utf8 from 'to-utf-8'
import Assets from './assets'
import capitalGainsCalculateStream from './capital-gains-calculate-stream'

@@ -19,4 +20,5 @@ import capitalGainsFormatStream from './capital-gains-format-stream'

import tradeParseStream from './trade-parse-stream'
import tradeSeparateStream from './trade-separate-stream'
import tradeTransactionsStream from './trade-transactions-stream'
import tradeValueStream from './trade-value-stream'
import transactionFilterStream from './transaction-filter-stream'

@@ -28,6 +30,6 @@ export default function main(args) {

{
short: 'f',
long: 'forward',
short: 'a',
long: ['assets', 'asset'],
argument: 'spec',
description: 'Define the initial balance and ACB of the assets.'
description: 'Only consider the specified assets.'
},

@@ -41,2 +43,8 @@ {

{
short: 'i',
long: 'init',
argument: 'spec',
description: 'Define the initial balance and ACB of the assets.'
},
{
short: 'm',

@@ -81,4 +89,4 @@ long: 'html',

usage: {
footer: fs.readFileSync(__dirname + '/../res/cmdline_footer.txt', 'utf8').replace(/([.,;:])\r?\n/g, '$1 '),
header: fs.readFileSync(__dirname + '/../res/cmdline_header.txt', 'utf8').replace(/([.,;:])\r?\n/g, '$1 '),
footer: fs.readFileSync(__dirname + '/../res/cmdline_footer.txt', 'utf8').replace(/([.,;:])\r?\n([A-Za-z])/g, '$1 $2'),
header: fs.readFileSync(__dirname + '/../res/cmdline_header.txt', 'utf8').replace(/([.,;:])\r?\n([A-Za-z])/g, '$1 $2'),
program: 'crypto-tax-calculator',

@@ -101,6 +109,6 @@ spec: '[option]... <csv-file>...'

// Parse the initial balance and ACB of each asset.
let forward = null
if (opts.options.forward)
forward = fromEntries(opts.options.forward.value.split(',')
// 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(',')
.map(function(spec) {

@@ -136,14 +144,22 @@ let [asset, balance, acb] = spec.split(':')

// Continue creating the stream.
stream = stream
.pipe(tradeValueStream({
history,
verbose: !!opts.options.verbose,
web: !!opts.options.web
}))
.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.
stream = multiStream([
fs.createReadStream(__dirname + '/../res/output_header.md'),
stream
.pipe(tradeValueStream({
history,
verbose: !!opts.options.verbose,
web: !!opts.options.web
}))
.pipe(tradeSeparateStream())
.pipe(capitalGainsCalculateStream({
forward
forwardByAsset
}))

@@ -150,0 +166,0 @@ .pipe(capitalGainsFormatStream()),

@@ -16,8 +16,8 @@ 'use strict'

* @property {number} quoteAmount The amount of the quote currency.
* @property {number} [value] The value of the assets, in Canadian dollars.
* @property {boolean} sell True if the trade represents a sale.
* @property {number} time The time at which the trade occurred, as a UNIX timestamp.
* @property {string} feeAsset The currency of the trading fee.
* @property {number} feeAmount The amount of the trading fee.
* @property {number} [value] The value of the assets, in Canadian dollars.
* @property {number} [fee] The value of the trading fee, in Canadian dollars.
* @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.
*/

@@ -135,2 +135,5 @@

let quantity = TradeParseStream._parseNumber(chunk.Quantity)
let quantityRemaining = TradeParseStream._parseNumber(chunk.QuantityRemaining)
this.push({

@@ -141,3 +144,3 @@ exchange: 'Bittrex',

baseAmount: TradeParseStream._parseNumber(chunk.Price),
quoteAmount: TradeParseStream._parseNumber(chunk.Quantity),
quoteAmount: quantity - quantityRemaining,
sell: chunk.OrderType.includes('SELL'),

@@ -157,41 +160,44 @@ time: TradeParseStream._parseTime(chunk.TimeStamp),

if (chunk.type === 'trade') {
// Normalize the properties of the chunk.
chunk = {
asset: Assets.normalizeCode(chunk.asset),
amount: TradeParseStream._parseNumber(chunk.amount),
time: TradeParseStream._parseTime(chunk.time),
fee: TradeParseStream._parseNumber(chunk.fee)
// We only care about trades.
if (chunk.type !== 'trade') {
if (chunks.length > 0) {
console.log('WARNING: Found unpaired trade chunk.')
chunks.length = 0
}
return
}
// Process two consecutive trade chunks as a single trade.
chunks.push(chunk)
if (chunks.length === 2) {
// Ensure the chunks have the same timestamp.
if (chunks[0].time !== chunks[1].time)
console.log('WARNING: Found paired trade chunks with different timestamps.')
// Normalize the properties of the chunk.
chunk = {
asset: Assets.normalizeCode(chunk.asset),
amount: TradeParseStream._parseNumber(chunk.amount),
time: TradeParseStream._parseTime(chunk.time),
fee: TradeParseStream._parseNumber(chunk.fee)
}
// Determine which chunks represent the base and quote of the currency pair.
let priorities = chunks.map(c => Assets.getPriority(c.asset))
let isCurrencyPairReversed = priorities[0] < priorities[1]
let baseChunk = chunks[+!isCurrencyPairReversed]
let quoteChunk = chunks[+isCurrencyPairReversed]
// Process two consecutive trade chunks as a single trade.
chunks.push(chunk)
if (chunks.length === 2) {
// Ensure the chunks have the same timestamp.
if (chunks[0].time !== chunks[1].time)
console.log('WARNING: Found paired trade chunks with different timestamps.')
this.push({
exchange: 'Kraken',
baseAsset: baseChunk.asset,
quoteAsset: quoteChunk.asset,
baseAmount: Math.abs(baseChunk.amount),
quoteAmount: Math.abs(quoteChunk.amount),
sell: baseChunk.amount > 0,
time: baseChunk.time,
feeAsset: baseChunk.asset,
feeAmount: baseChunk.fee
})
// Determine which chunks represent the base and quote of the currency pair.
let priorities = chunks.map(c => Assets.getPriority(c.asset))
let isCurrencyPairReversed = priorities[0] < priorities[1]
let baseChunk = chunks[+!isCurrencyPairReversed]
let quoteChunk = chunks[+isCurrencyPairReversed]
chunks.length = 0
}
}
else if (chunks.length > 0) {
console.log('WARNING: Found unpaired trade chunk.')
this.push({
exchange: 'Kraken',
baseAsset: baseChunk.asset,
quoteAsset: quoteChunk.asset,
baseAmount: Math.abs(baseChunk.amount),
quoteAmount: Math.abs(quoteChunk.amount),
sell: baseChunk.amount > 0,
time: baseChunk.time,
feeAsset: baseChunk.asset,
feeAmount: baseChunk.fee
})
chunks.length = 0

@@ -198,0 +204,0 @@ }

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

// Calculate the value of the fee.
chunk.fee = (
chunk.feeValue = (
chunk.feeAsset === chunk.baseAsset ? chunk.value * chunk.feeAmount / chunk.baseAmount :

@@ -48,0 +48,0 @@ chunk.feeAsset === chunk.quoteAsset ? chunk.value * chunk.feeAmount / chunk.quoteAmount :

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