diff --git a/components/TradesTable/index.tsx b/components/TradesTable/index.tsx index c92de882..d244c28d 100644 --- a/components/TradesTable/index.tsx +++ b/components/TradesTable/index.tsx @@ -55,6 +55,7 @@ export class TradesTable extends React.Component [ @@ -66,6 +67,7 @@ export class TradesTable extends React.Component{trade.boughtCurrency}, {(trade.amountSold / trade.rate).toFixed(8)}, {`${trade.transactionFee} ${trade.transactionFeeCurrency}`}, + {`${trade.tradeFee} ${trade.tradeFeeCurrency}`}, , ])} /> diff --git a/src/parsers/trades/coinlist/index.ts b/src/parsers/trades/coinlist/index.ts new file mode 100644 index 00000000..8b4760ca --- /dev/null +++ b/src/parsers/trades/coinlist/index.ts @@ -0,0 +1,93 @@ +import { getCSVData } from '../../'; +import { EXCHANGES, IImport, IPartialTrade, ITrade } from '../../../types'; +import { createID } from '../../utils'; + +interface ICoinList { + Date: string; + Description: string; + Asset: string; + Amount: string; + Balance: string; +} + +interface ICoinListPro { + portfolio: string; + type: string; + time: string; + amount: string; + balance: string; + 'amount/balance unit': string; + transaction_id: string +} + +export default async function processData(importDetails: IImport): Promise { + if (importDetails.data.split(',')[0] === 'portfolio') { + console.log('Detected CoinListPro') + return processProData(importDetails); + } + const data: ICoinList[] = await getCSVData(importDetails.data) as ICoinList[]; + const internalFormat: ITrade[] = []; + for (let i = 0; i < data.length; i++) { + const trade = data[i]; + const tradeToAdd: IPartialTrade = { + date : new Date(trade.Date).getTime(), + exchange : EXCHANGES.CoinList, + }; + let descriptionSplit = trade.Description.split(' '); + let type = descriptionSplit[0]; + switch (type) { + case 'Sold': + case 'Bought': { + i++; + tradeToAdd.boughtCurrency = trade.Asset; + tradeToAdd.soldCurrency = data[i].Asset; + tradeToAdd.amountSold = Math.abs(parseFloat(data[i].Amount)); + tradeToAdd.rate = Math.abs(parseFloat(data[i].Amount) / parseFloat(trade.Amount)); + tradeToAdd.ID = createID(tradeToAdd); + tradeToAdd.exchangeID = tradeToAdd.ID; + internalFormat.push(tradeToAdd as ITrade); + continue; + } + // TODO: Withdrawal, Distribution, Deposit, Hold + default: { + console.log(`Ignored ${tradeToAdd.exchange} trade of type ${type}`); + break; + } + } + } + return internalFormat; +} + +export async function processProData(importDetails: IImport): Promise { + const data: ICoinListPro[] = await getCSVData(importDetails.data) as ICoinListPro[]; + const internalFormat: ITrade[] = []; + for (let i = 0; i < data.length; i++) { + const trade = data[i]; + const tradeToAdd: IPartialTrade = { + date : new Date(trade.time).getTime(), + exchange : EXCHANGES.CoinList, + }; + switch (trade.type) { + case 'match': { + i++; + tradeToAdd.boughtCurrency = trade.balance; + tradeToAdd.tradeFee = Math.abs(parseFloat(data[i].amount)); + tradeToAdd.tradeFeeCurrency = data[i].balance; + i++; + tradeToAdd.soldCurrency = data[i].balance; + tradeToAdd.amountSold = Math.abs(parseFloat(data[i].amount)); + tradeToAdd.rate = Math.abs(parseFloat(data[i].amount) / parseFloat(trade.amount)); + tradeToAdd.ID = createID(tradeToAdd); + tradeToAdd.exchangeID = tradeToAdd.ID; + internalFormat.push(tradeToAdd as ITrade); + continue; + } + // TODO: deposit, withdrawal + default: { + console.log(`Ignored ${tradeToAdd.exchange} trade of type ${trade.type}`); + break; + } + } + } + return internalFormat; +} diff --git a/src/parsers/trades/index.ts b/src/parsers/trades/index.ts index 30b720d4..d7c80fdf 100644 --- a/src/parsers/trades/index.ts +++ b/src/parsers/trades/index.ts @@ -2,6 +2,7 @@ import { EXCHANGES, ExchangesTradeHeaders, IImport, ITrade } from '@types'; import * as crypto from 'crypto'; import binanceParser from './binance'; import bittrexParser from './bittrex'; +import coinListParser from './coinlist'; import geminiParser from './gemini'; import krakenParser from './kraken'; import poloniexParser from './poloniex'; @@ -10,6 +11,7 @@ import revolutParser from './revolut'; const parserMapping: {[key in EXCHANGES]: any} = { [EXCHANGES.Binance]: binanceParser, [EXCHANGES.Bittrex]: bittrexParser, + [EXCHANGES.CoinList]: coinListParser, [EXCHANGES.Gemini]: geminiParser, [EXCHANGES.Kraken]: krakenParser, [EXCHANGES.Poloniex]: poloniexParser, @@ -24,7 +26,7 @@ export default async function processTradesImport(importDetails: IImport): Promi const headers = importDetails.data.substr(0, importDetails.data.indexOf('\n')); const headersHash = crypto.createHash('sha256').update(headers).digest('hex'); for (const key in ExchangesTradeHeaders) { - if (ExchangesTradeHeaders[key] === headersHash) { + if (ExchangesTradeHeaders[key].split(';').includes(headersHash)) { return processTradesImport({ ...importDetails, location: key, diff --git a/src/types/locations.ts b/src/types/locations.ts index c0053d4b..c490cc63 100644 --- a/src/types/locations.ts +++ b/src/types/locations.ts @@ -2,6 +2,7 @@ export type Location = EXCHANGES | string; export enum EXCHANGES { Bittrex = 'BITTREX', + CoinList = 'COINLIST', Gemini= 'GEMINI', Poloniex = 'POLONIEX', Kraken = 'KRAKEN', @@ -15,6 +16,8 @@ export enum IncomeImportTypes { export enum ExchangesTradeHeaders { BITTREX = '07230399aaa8d1f15e88e38bd43a01c5ef1af6c1f9131668d346e196ff090d80', + COINLIST = 'a700f71b8629872a0d8d5320612aedcb53f58cc55937eb146124a14360d991f1;\ +9eccf556ea42d14253a223980bf0023f6cc0d21320db187f6b9b6bbf59ea8678', GEMINI = '996edee25db7f3d1dd16c83c164c6cff8c6d0f5d6b3aafe6d1700f2a830f6c9e', POLONIEX = 'd7484d726e014edaa059c0137ac91183a7eaa9ee5d52713aa48bb4104b01afb0', KRAKEN = '85bf27e799cc0a30fe5b201cd6a4724e4a52feb433f41a1e8b046924e3bf8dc5', diff --git a/src/types/trade.ts b/src/types/trade.ts index 07731197..1de8b25b 100644 --- a/src/types/trade.ts +++ b/src/types/trade.ts @@ -13,6 +13,8 @@ export interface ITrade { ID: string; transactionFee: number; transactionFeeCurrency: string; + tradeFee?: number; + tradeFeeCurrency?: string; } export interface ITradeWithFiatRate extends ITrade {