@ -29,15 +29,19 @@ import { Injectable, Logger } from '@nestjs/common';
import { SymbolProfile } from '@prisma/client' ;
import { SymbolProfile } from '@prisma/client' ;
import { Big } from 'big.js' ;
import { Big } from 'big.js' ;
import {
import {
addHours ,
differenceInDays ,
differenceInDays ,
eachDayOfInterval ,
eachDayOfInterval ,
format ,
format ,
isAfter ,
isSameDay ,
isSameDay ,
subDays
subDays
} from 'date-fns' ;
} from 'date-fns' ;
import { isNumber , last , uniqBy } from 'lodash' ;
import { isNumber , last , uniqBy } from 'lodash' ;
import ms from 'ms' ;
import ms from 'ms' ;
import { BenchmarkValue } from './interfaces/benchmark-value.interface' ;
@Injectable ( )
@Injectable ( )
export class BenchmarkService {
export class BenchmarkService {
private readonly CACHE_KEY_BENCHMARKS = 'BENCHMARKS' ;
private readonly CACHE_KEY_BENCHMARKS = 'BENCHMARKS' ;
@ -92,99 +96,26 @@ export class BenchmarkService {
enableSharing = false ,
enableSharing = false ,
useCache = true
useCache = true
} = { } ) : Promise < BenchmarkResponse [ ' benchmarks ' ] > {
} = { } ) : Promise < BenchmarkResponse [ ' benchmarks ' ] > {
let benchmarks : BenchmarkResponse [ 'benchmarks' ] ;
if ( useCache ) {
if ( useCache ) {
try {
try {
benchmarks = JSON . parse (
const cachedBenchmarkValue = await this . redisCacheService . get (
await this . redisCacheService . get ( this. CACHE_KEY_BENCHMARKS )
this. CACHE_KEY_BENCHMARKS
) ;
) ;
if ( benchmarks ) {
const { benchmarks , expiration } : BenchmarkValue =
return benchmarks ;
JSON . parse ( cachedBenchmarkValue ) ;
}
} catch { }
}
const benchmarkAssetProfiles = await this . getBenchmarkAssetProfiles ( {
enableSharing
} ) ;
const promisesAllTimeHighs : Promise < { date : Date ; marketPrice : number } > [ ] =
[ ] ;
const promisesBenchmarkTrends : Promise < {
trend50d : BenchmarkTrend ;
trend200d : BenchmarkTrend ;
} > [ ] = [ ] ;
const quotes = await this . dataProviderService . getQuotes ( {
items : benchmarkAssetProfiles.map ( ( { dataSource , symbol } ) = > {
return { dataSource , symbol } ;
} ) ,
requestTimeout : ms ( '30 seconds' ) ,
useCache : false
} ) ;
for ( const { dataSource , symbol } of benchmarkAssetProfiles ) {
promisesAllTimeHighs . push (
this . marketDataService . getMax ( { dataSource , symbol } )
) ;
promisesBenchmarkTrends . push (
this . getBenchmarkTrends ( { dataSource , symbol } )
) ;
}
const [ allTimeHighs , benchmarkTrends ] = await Promise . all ( [
Promise . all ( promisesAllTimeHighs ) ,
Promise . all ( promisesBenchmarkTrends )
] ) ;
let storeInCache = useCache ;
benchmarks = allTimeHighs . map ( ( allTimeHigh , index ) = > {
if ( isAfter ( new Date ( ) , new Date ( expiration ) ) ) {
const { marketPrice } =
this . calculateAndCacheBenchmarks ( {
quotes [ benchmarkAssetProfiles [ index ] . symbol ] ? ? { } ;
enableSharing
} ) ;
let performancePercentFromAllTimeHigh = 0 ;
}
if ( allTimeHigh ? . marketPrice && marketPrice ) {
performancePercentFromAllTimeHigh = this . calculateChangeInPercentage (
allTimeHigh . marketPrice ,
marketPrice
) ;
} else {
storeInCache = false ;
}
return {
dataSource : benchmarkAssetProfiles [ index ] . dataSource ,
marketCondition : this.getMarketCondition (
performancePercentFromAllTimeHigh
) ,
name : benchmarkAssetProfiles [ index ] . name ,
performances : {
allTimeHigh : {
date : allTimeHigh?.date ,
performancePercent :
performancePercentFromAllTimeHigh >= 0
? 0
: performancePercentFromAllTimeHigh
}
} ,
symbol : benchmarkAssetProfiles [ index ] . symbol ,
trend50d : benchmarkTrends [ index ] . trend50d ,
trend200d : benchmarkTrends [ index ] . trend200d
} ;
} ) ;
if ( storeInCache ) {
return benchmarks ;
await this . redisCacheService . set (
} catch { }
this . CACHE_KEY_BENCHMARKS ,
JSON . stringify ( benchmarks ) ,
ms ( '2 hours' ) / 1000
) ;
}
}
return benchmarks ;
return this . calculateAndCacheBenchmarks ( { enableSharing } ) ;
}
}
public async getBenchmarkAssetProfiles ( {
public async getBenchmarkAssetProfiles ( {
@ -422,6 +353,95 @@ export class BenchmarkService {
} ;
} ;
}
}
private async calculateAndCacheBenchmarks ( {
enableSharing = false
} ) : Promise < BenchmarkResponse [ ' benchmarks ' ] > {
const benchmarkAssetProfiles = await this . getBenchmarkAssetProfiles ( {
enableSharing
} ) ;
const promisesAllTimeHighs : Promise < { date : Date ; marketPrice : number } > [ ] =
[ ] ;
const promisesBenchmarkTrends : Promise < {
trend50d : BenchmarkTrend ;
trend200d : BenchmarkTrend ;
} > [ ] = [ ] ;
const quotes = await this . dataProviderService . getQuotes ( {
items : benchmarkAssetProfiles.map ( ( { dataSource , symbol } ) = > {
return { dataSource , symbol } ;
} ) ,
requestTimeout : ms ( '30 seconds' ) ,
useCache : false
} ) ;
for ( const { dataSource , symbol } of benchmarkAssetProfiles ) {
promisesAllTimeHighs . push (
this . marketDataService . getMax ( { dataSource , symbol } )
) ;
promisesBenchmarkTrends . push (
this . getBenchmarkTrends ( { dataSource , symbol } )
) ;
}
const [ allTimeHighs , benchmarkTrends ] = await Promise . all ( [
Promise . all ( promisesAllTimeHighs ) ,
Promise . all ( promisesBenchmarkTrends )
] ) ;
let storeInCache = true ;
const benchmarks = allTimeHighs . map ( ( allTimeHigh , index ) = > {
const { marketPrice } =
quotes [ benchmarkAssetProfiles [ index ] . symbol ] ? ? { } ;
let performancePercentFromAllTimeHigh = 0 ;
if ( allTimeHigh ? . marketPrice && marketPrice ) {
performancePercentFromAllTimeHigh = this . calculateChangeInPercentage (
allTimeHigh . marketPrice ,
marketPrice
) ;
} else {
storeInCache = false ;
}
return {
dataSource : benchmarkAssetProfiles [ index ] . dataSource ,
marketCondition : this.getMarketCondition (
performancePercentFromAllTimeHigh
) ,
name : benchmarkAssetProfiles [ index ] . name ,
performances : {
allTimeHigh : {
date : allTimeHigh?.date ,
performancePercent :
performancePercentFromAllTimeHigh >= 0
? 0
: performancePercentFromAllTimeHigh
}
} ,
symbol : benchmarkAssetProfiles [ index ] . symbol ,
trend50d : benchmarkTrends [ index ] . trend50d ,
trend200d : benchmarkTrends [ index ] . trend200d
} ;
} ) ;
if ( storeInCache ) {
const expiration = addHours ( new Date ( ) , 2 ) ;
await this . redisCacheService . set (
this . CACHE_KEY_BENCHMARKS ,
JSON . stringify ( < BenchmarkValue > {
benchmarks ,
expiration : expiration.getTime ( )
} ) ,
ms ( '12 hours' ) / 1000
) ;
}
return benchmarks ;
}
private getMarketCondition (
private getMarketCondition (
aPerformanceInPercent : number
aPerformanceInPercent : number
) : Benchmark [ 'marketCondition' ] {
) : Benchmark [ 'marketCondition' ] {