You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

127 lines
2.9 KiB

import { getYesterday } from '@ghostfolio/helper';
import { Injectable } from '@nestjs/common';
import * as bent from 'bent';
import * as cheerio from 'cheerio';
import { format } from 'date-fns';
import { DataProviderInterface } from '../../interfaces/data-provider.interface';
import { Granularity } from '../../interfaces/granularity.type';
import {
IDataProviderHistoricalResponse,
IDataProviderResponse,
MarketState
} from '../../interfaces/interfaces';
import { PrismaService } from '../../prisma.service';
@Injectable()
export class GhostfolioScraperApiService implements DataProviderInterface {
private static NUMERIC_REGEXP = /[-]{0,1}[\d]*[.,]{0,1}[\d]+/g;
public constructor(private prisma: PrismaService) {}
public async get(
aSymbols: string[]
): Promise<{ [symbol: string]: IDataProviderResponse }> {
if (aSymbols.length <= 0) {
return {};
}
try {
const symbol = aSymbols[0];
const scraperConfig = await this.getScraperConfig(symbol);
const { marketPrice } = await this.prisma.marketData.findFirst({
orderBy: {
date: 'desc'
},
where: {
symbol
}
});
return {
[symbol]: {
marketPrice,
currency: scraperConfig?.currency,
marketState: MarketState.delayed,
name: scraperConfig?.name
}
};
} catch (error) {
console.error(error);
}
return {};
}
public async getHistorical(
aSymbols: string[],
aGranularity: Granularity = 'day',
from: Date,
to: Date
): Promise<{
[symbol: string]: { [date: string]: IDataProviderHistoricalResponse };
}> {
if (aSymbols.length <= 0) {
return {};
}
try {
const symbol = aSymbols[0];
const scraperConfig = await this.getScraperConfig(symbol);
const get = bent(scraperConfig?.url, 'GET', 'string', 200, {});
const html = await get();
const $ = cheerio.load(html);
const value = this.extractNumberFromString(
$(scraperConfig?.selector).text()
);
return {
[symbol]: {
[format(getYesterday(), 'yyyy-MM-dd')]: {
marketPrice: value
}
}
};
} catch (error) {
console.error(error);
}
return {};
}
private extractNumberFromString(aString: string): number {
try {
const [numberString] = aString.match(
GhostfolioScraperApiService.NUMERIC_REGEXP
);
return parseFloat(numberString.trim());
} catch {
return undefined;
}
}
private async getScraperConfig(aSymbol: string) {
try {
const {
value: scraperConfigString
} = await this.prisma.property.findFirst({
select: {
value: true
},
where: { key: 'SCRAPER_CONFIG' }
});
return JSON.parse(scraperConfigString).find((item) => {
return item.symbol === aSymbol;
});
} catch {}
return {};
}
}