Browse Source

Refactoring

pull/1736/head
Thomas 3 years ago
parent
commit
95e0f546d9
  1. 3
      apps/api/src/app/order/order.service.ts
  2. 190
      apps/api/src/services/data-provider/coingecko/coingecko.service.ts

3
apps/api/src/app/order/order.service.ts

@ -110,9 +110,6 @@ export class OrderService {
dataSource, dataSource,
symbol: id symbol: id
}; };
} else {
data.SymbolProfile.connectOrCreate.create.symbol =
data.SymbolProfile.connectOrCreate.create.symbol.toUpperCase();
} }
await this.dataGatheringService.addJobToQueue( await this.dataGatheringService.addJobToQueue(

190
apps/api/src/services/data-provider/coingecko/coingecko.service.ts

@ -15,88 +15,58 @@ import {
SymbolProfile SymbolProfile
} from '@prisma/client'; } from '@prisma/client';
import bent from 'bent'; import bent from 'bent';
import { format, differenceInDays, addDays, subDays } from 'date-fns'; import { format, fromUnixTime, getUnixTime } from 'date-fns';
@Injectable() @Injectable()
export class CoinGeckoService implements DataProviderInterface { export class CoinGeckoService implements DataProviderInterface {
private readonly URL = 'https://api.coingecko.com/api/v3';
private baseCurrency: string; private baseCurrency: string;
private DB = {}; private readonly URL = 'https://api.coingecko.com/api/v3';
public constructor( public constructor(
private readonly configurationService: ConfigurationService private readonly configurationService: ConfigurationService
) { ) {
this.baseCurrency = this.configurationService this.baseCurrency = this.configurationService.get('BASE_CURRENCY');
.get('BASE_CURRENCY')
.toUpperCase();
this.DB = {};
} }
public canHandle(symbol: string) { public canHandle(symbol: string) {
return true; return true;
} }
public async getDividends({
from,
granularity = 'day',
symbol,
to
}: {
from: Date;
granularity: Granularity;
symbol: string;
to: Date;
}) {
return {};
}
public async getAssetProfile( public async getAssetProfile(
aSymbol: string aSymbol: string
): Promise<Partial<SymbolProfile>> { ): Promise<Partial<SymbolProfile>> {
return { const response: Partial<SymbolProfile> = {
assetClass: AssetClass.CASH, assetClass: AssetClass.CASH,
assetSubClass: AssetSubClass.CRYPTOCURRENCY, assetSubClass: AssetSubClass.CRYPTOCURRENCY,
currency: this.baseCurrency.toUpperCase(), currency: this.baseCurrency,
dataSource: this.getName(), dataSource: this.getName(),
name: aSymbol,
symbol: aSymbol symbol: aSymbol
}; };
}
private async populateDatabase(datefrom: Date, symbol: string) { try {
let start_day; const get = bent(`${this.URL}/coins/${aSymbol}`, 'GET', 'json', 200);
let end_day; const { name } = await get();
datefrom.setHours(0, 0, 1);
start_day = Math.round(datefrom.getTime() / 1000); response.name = name;
end_day = Math.round(new Date().getTime() / 1000); } catch (error) {
const targeturl = `${ Logger.error(error, 'CoinGeckoService');
this.URL
}/coins/${symbol.toLowerCase()}/market_chart/range?vs_currency=${this.baseCurrency.toLowerCase()}&from=${start_day}&to=${end_day}`;
const req = bent(targeturl, 'GET', 'json', 200);
const response = await req();
if (response.prices.length) {
for (const iter of response.prices) {
let day = new Date(iter[0]);
day.setHours(0, 0, 1, 1);
let dayepoch = Math.round(day.getTime() / 1000);
this.DB[dayepoch] = iter[1];
}
}
} }
private async getDayStat(datein: Date, symbol: string) { return response;
let out = { marketPrice: 0 };
let prevday = subDays(datein, 1);
datein.setHours(0, 0, 1, 1);
let start_day = Math.round(datein.getTime() / 1000);
let prev_day = Math.round(prevday.getTime() / 1000);
out['marketPrice'] = this.DB[start_day];
if (prev_day in this.DB) {
out['performance'] = this.DB[start_day] / this.DB[prev_day];
} else {
out['performance'] = 0;
} }
return out;
public async getDividends({
from,
granularity = 'day',
symbol,
to
}: {
from: Date;
granularity: Granularity;
symbol: string;
to: Date;
}) {
return {};
} }
public async getHistorical( public async getHistorical(
@ -107,22 +77,44 @@ export class CoinGeckoService implements DataProviderInterface {
): Promise<{ ): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse }; [symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
}> { }> {
let out = {}; try {
out[aSymbol] = {}; const get = bent(
const totalDays = Math.abs(differenceInDays(from, to)) + 1; `${
await this.populateDatabase(from, aSymbol); this.URL
for (const iter of Array(totalDays).keys()) { }/coins/${aSymbol}/market_chart/range?vs_currency=${this.baseCurrency.toLowerCase()}&from=${getUnixTime(
let day = addDays(from, iter); from
let datestr = format(day, DATE_FORMAT); )}&to=${getUnixTime(to)}`,
out[aSymbol][datestr] = await this.getDayStat(day, aSymbol); 'GET',
'json',
200
);
const { prices } = await get();
const result: {
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
} = {
[aSymbol]: {}
};
for (const [timestamp, marketPrice] of prices) {
result[aSymbol][format(fromUnixTime(timestamp / 1000), DATE_FORMAT)] = {
marketPrice
};
} }
return out; return result;
} catch (error) {
throw new Error(
`Could not get historical market data for ${aSymbol} (${this.getName()}) from ${format(
from,
DATE_FORMAT
)} to ${format(to, DATE_FORMAT)}: [${error.name}] ${error.message}`
);
}
} }
public getMaxNumberOfSymbolsPerRequest() { public getMaxNumberOfSymbolsPerRequest() {
// Safe Rate Limit: https://www.coingecko.com/en/api/pricing#general return 50;
return 20;
} }
public getName(): DataSource { public getName(): DataSource {
@ -132,56 +124,68 @@ export class CoinGeckoService implements DataProviderInterface {
public async getQuotes( public async getQuotes(
aSymbols: string[] aSymbols: string[]
): Promise<{ [symbol: string]: IDataProviderResponse }> { ): Promise<{ [symbol: string]: IDataProviderResponse }> {
var results = {}; const results: { [symbol: string]: IDataProviderResponse } = {};
if (aSymbols.length <= 0) { if (aSymbols.length <= 0) {
return {}; return {};
} }
try { try {
for (const coin of aSymbols) { const get = bent(
const req = bent( `${this.URL}/simple/price?ids=${aSymbols.join(
`${ ','
this.URL )}&vs_currencies=${this.baseCurrency.toLowerCase()}`,
}/simple/price?ids=${coin.toLowerCase()}&vs_currencies=${this.baseCurrency.toLowerCase()}`,
'GET', 'GET',
'json', 'json',
200 200
); );
const response = await req(); const response = await get();
const price =
response[coin.toLowerCase()][this.baseCurrency.toLowerCase()];
results[coin] = { for (const symbol in response) {
if (Object.prototype.hasOwnProperty.call(response, symbol)) {
results[symbol] = {
currency: this.baseCurrency, currency: this.baseCurrency,
dataSource: DataSource.COINGECKO, dataSource: DataSource.COINGECKO,
marketPrice: price, marketPrice: response[symbol][this.baseCurrency.toLowerCase()],
marketState: 'closed' marketState: 'open'
}; };
} }
}
return results;
} catch (error) { } catch (error) {
Logger.error(error, 'CoinGecko'); Logger.error(error, 'CoinGeckoService');
return {};
} }
return results;
} }
public async search(aQuery: string): Promise<{ items: LookupItem[] }> { public async search(aQuery: string): Promise<{ items: LookupItem[] }> {
const items: LookupItem[] = []; let items: LookupItem[] = [];
if (aQuery.length <= 2) { if (aQuery.length <= 2) {
return { items }; return { items };
} }
const req = bent(`${this.URL}/search?query=${aQuery}`, 'GET', 'json', 200);
const response = await req(); try {
for (const coiniter of response.coins) { const get = bent(
if (coiniter.id.toLowerCase().includes(aQuery)) { `${this.URL}/search?query=${aQuery}`,
items.push({ 'GET',
symbol: coiniter.id.toUpperCase(), 'json',
200
);
const { coins } = await get();
items = coins.map(({ id: symbol, name }) => {
return {
name,
symbol,
currency: this.baseCurrency, currency: this.baseCurrency,
dataSource: this.getName(), dataSource: this.getName()
name: `${coiniter.name} (From CoinGecko)` };
}); });
} catch (error) {
Logger.error(error, 'CoinGeckoService');
} }
}
return { items }; return { items };
} }
} }

Loading…
Cancel
Save