Skip to content

Commit

Permalink
feat: add cryptocompare api
Browse files Browse the repository at this point in the history
  • Loading branch information
tehfonsi committed May 17, 2024
1 parent 2a4b253 commit b592970
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 25 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@ Open the [Stackblitz Taxler Demo](https://stackblitz.com/edit/taxler-demo-1-0-4?
- [Bitpanda](./src/plugins/bitpanda.ts)
- [Binance](./src/plugins/binance.ts)

## Price APIs

Currently supported APIs to fetch historical data are:
- [CoinGecko API](https://www.coingecko.com/en/api)!
- [CryptoCompare API](https://min-api.cryptocompare.com/)!

Enter your API key in the `config.json` file which is generated with the `init` command.

## Contribute

You can contribute to this project by writing a new plugin.
Expand All @@ -47,6 +55,4 @@ Also please use my referral code when creating an account for
- [Youhodler](https://track.youhodler.com/click?pid=875&offer_id=2&sub2=github)
- [Bitpanda](https://www.bitpanda.com/?ref=253327100783639469)
- [Binance](https://www.binance.com/en/activity/referral-entry?fromActivityPage=true&ref=LIMIT_HNR7LRVP) - Code `LIMIT_HNR7LRVP`

---
Crypto lookups are powered by [CoinGecko API](https://www.coingecko.com/en/api)!

98 changes: 98 additions & 0 deletions src/apis/cryptocompare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { Config } from '../common/config';
import { DI } from '../di.config';
import { Coin } from '../plugins/common/plugin';
import Api, { Price } from './api';

const BASE_URL = 'https://min-api.cryptocompare.com';

let coinList: any[] | null = null;
let coinLookup = new Map<string, Coin>();

export default class CryptoCompare extends Api {
private _config: Config = DI().get('Config');

public async findMatch(symbol: string, name?: string): Promise<Coin | null> {
if (coinLookup.has(symbol)) {
return Promise.resolve(coinLookup.get(symbol)!);
}
symbol = symbol.toLowerCase();
if (!coinList) {
const response = (await this.getJson(
BASE_URL + `/data/all/coinlist`
)) as Array<any>;
if (!response) {
return Promise.resolve(null);
}

coinList = Object.values((response as any).Data);
}
const candidates = coinList.filter(
(coin: any) => coin.Symbol.toLowerCase() === symbol
);
let coin = null;
if (candidates.length > 1 && !name) {
console.warn(`Found multipe coins with symbol ${symbol}: `, candidates);
} else if (candidates.length > 1 && name) {
name = name.toLowerCase();
const candidate = coinList.find(
(coin: any) => coin.CoinName.toLowerCase() === name
);
coin = this._getCoin(candidate);
} else if (candidates.length > 0) {
coin = this._getCoin(candidates[0]);
} else if (this._config.fiat === symbol) {
coin = {
id: symbol.toUpperCase(),
name: symbol.toUpperCase(),
symbol: symbol.toUpperCase(),
};
} else {
console.warn(`Coin ${symbol} not found!`);
}
if (coin) {
coinLookup.set(symbol, coin);
}
return coin;
}

private _getCoin(match: any): Coin {
return {
id: match.Id as string,
name: match.CoinName as string,
symbol: (match.Symbol as string).toUpperCase(),
};
}

public async getPrice(
symbol: string,
date: Date,
name?: string
): Promise<Price | null> {
const coin = await this.findMatch(symbol, name);
if (!coin?.id) {
return null;
}
// return if it is fiat
if (coin.symbol.toLowerCase() === this._config.fiat.toLowerCase()) {
return {
price: 1,
coin,
};
}
const requestDate = new Date(date.getTime());
requestDate.setHours(12, 0, 0, 0);
const timestamp = requestDate.getTime() / 1000;
let url = `${BASE_URL}/data/pricehistorical?fsym=${coin.symbol}&tsyms=${this._config.fiat}&ts=${timestamp}`;
if (this._config.cryptocompare_api_key) {
url += `&api_key=${this._config.cryptocompare_api_key}`;
}
const data = await this.getJson(url);
if (!data || !data[coin.symbol][this._config.fiat.toUpperCase()]) {
return null;
}
return {
price: data[coin.symbol][this._config.fiat.toUpperCase()],
coin,
};
}
}
4 changes: 4 additions & 0 deletions src/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ export interface Config {
liquidityMining: number;
gift: number;
};
coingecko_api_key?: string;
cryptocompare_api_key?: string;
}

export const DEFAULT_CONFIG: Config = {
Expand All @@ -24,6 +26,8 @@ export const DEFAULT_CONFIG: Config = {
liquidityMining: 0,
gift: 0,
},
coingecko_api_key: '',
cryptocompare_api_key: '',
};

export default class ConfigHelper {
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const getTaxler = (path: string) => {

const init = async () => {
const isDev = process.env.NODE_ENV === 'development';
const defaultPath = isDev ? 'G:/Meine Ablage/Crypto/2021' : '.';
const defaultPath = isDev ? 'G:/Meine Ablage/Crypto/2023' : '.';

const argv = await yargs(hideBin(process.argv))
.scriptName('taxler')
Expand Down
19 changes: 17 additions & 2 deletions src/plugins/common/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { formateReportDate } from '../../common/utils';
import { DI } from '../../di.config';
import CommonIO from '../../io/common-io';
import CSVReader from '../../io/csv-reader';
import CryptoCompare from '../../apis/cryptocompare';
import Api from '../../apis/api';

export type Coin = {
id: string;
Expand Down Expand Up @@ -44,7 +46,16 @@ const REPORT_FILE = '_report.csv';

export default abstract class Plugin {
private _config: Config = DI().get('Config');
protected _api = new Coingecko();
protected _api: CryptoCompare | Coingecko;

constructor() {
if (this._config.cryptocompare_api_key) {
this._api = new CryptoCompare();
}
if (this._config.coingecko_api_key) {
this._api = new Coingecko();
}
}

public abstract getNames(): string[];

Expand Down Expand Up @@ -112,7 +123,11 @@ export default abstract class Plugin {
const report: string[][] = [];
const csv = CSVReader.read(path);

for (const line of csv) {
for (let i = 0; i < csv.length; i++) {
console.log(
`${this.getNames()[0]}: processing line ${i} of ${csv.length}`
);
const line = csv[i];
const transformedLine = await this.convertRow(line);
if (transformedLine) {
report.push(transformedLine);
Expand Down
52 changes: 33 additions & 19 deletions src/taxler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,25 @@ import PluginRegistry from './plugins/plugin-registry';
import Plugin, { COLUMN } from './plugins/common/plugin';
import { EOL } from 'os';
import { injectable } from 'inversify';
import ConfigHelper from './common/config';
import ConfigHelper, { Config } from './common/config';
import Coingecko from './apis/coingecko';
import CryptoCompare from './apis/cryptocompare';
import { DI } from './di.config';

@injectable()
export default class Taxler {
private _config: Config = DI().get('Config');
private _path: string;
private _api: Coingecko | CryptoCompare;

constructor() {
if (this._config.cryptocompare_api_key) {
this._api = new CryptoCompare();
}
if (this._config.coingecko_api_key) {
this._api = new Coingecko();
}
}

public setPath(path: string) {
this._path = path;
Expand Down Expand Up @@ -131,26 +144,27 @@ export default class Taxler {
console.log(line.join());
});

const api = new Coingecko();
let currentIncome = 0;
for (let { name, value: coinData } of Array.from(
coins,
([name, value]) => ({
name,
value,
})
)) {
const price = await api.getPrice(name, new Date(), coinData.name);
if (!price) {
continue;
if (this._api) {
let currentIncome = 0;
for (let { name, value: coinData } of Array.from(
coins,
([name, value]) => ({
name,
value,
})
)) {
const price = await this._api.getPrice(name, new Date(), coinData.name);
if (!price) {
continue;
}
const currentValue = price.price * coinData.total;
currentIncome += currentValue;
console.log(`${name}: ${currentValue}`);
}
const currentValue = price.price * coinData.total;
currentIncome += currentValue;
console.log(`${name}: ${currentValue}`);
console.log(
`Income: ${income} (current value: ${currentIncome}), Taxes: ${taxes}`
);
}
console.log(
`Income: ${income} (current value: ${currentIncome}), Taxes: ${taxes}`
);
}

private _getPlugin(name: string): Plugin | undefined {
Expand Down

0 comments on commit b592970

Please sign in to comment.