Feature/support update of historical data (#557)
* Support update of historical data * Update changelog
This commit is contained in:
parent
aca0d77e91
commit
ddce8cc7f9
@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added a line chart to the historical data view in the admin control panel
|
- Added a line chart to the historical data view in the admin control panel
|
||||||
|
- Supported the update of historical data in the admin control panel
|
||||||
|
|
||||||
## 1.91.0 - 18.12.2021
|
## 1.91.0 - 18.12.2021
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
|
import { DataGatheringService } from '@ghostfolio/api/services/data-gathering.service';
|
||||||
|
import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
|
||||||
import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
|
import { PropertyDto } from '@ghostfolio/api/services/property/property.dto';
|
||||||
import { PropertyService } from '@ghostfolio/api/services/property/property.service';
|
|
||||||
import {
|
import {
|
||||||
AdminData,
|
AdminData,
|
||||||
AdminMarketData,
|
AdminMarketData,
|
||||||
@ -22,16 +22,18 @@ import {
|
|||||||
import { REQUEST } from '@nestjs/core';
|
import { REQUEST } from '@nestjs/core';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
import { DataSource, MarketData } from '@prisma/client';
|
import { DataSource, MarketData } from '@prisma/client';
|
||||||
import { isDate, isValid } from 'date-fns';
|
import { isDate } from 'date-fns';
|
||||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||||
|
|
||||||
import { AdminService } from './admin.service';
|
import { AdminService } from './admin.service';
|
||||||
|
import { UpdateMarketDataDto } from './update-market-data.dto';
|
||||||
|
|
||||||
@Controller('admin')
|
@Controller('admin')
|
||||||
export class AdminController {
|
export class AdminController {
|
||||||
public constructor(
|
public constructor(
|
||||||
private readonly adminService: AdminService,
|
private readonly adminService: AdminService,
|
||||||
private readonly dataGatheringService: DataGatheringService,
|
private readonly dataGatheringService: DataGatheringService,
|
||||||
|
private readonly marketDataService: MarketDataService,
|
||||||
@Inject(REQUEST) private readonly request: RequestWithUser
|
@Inject(REQUEST) private readonly request: RequestWithUser
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ -173,7 +175,7 @@ export class AdminController {
|
|||||||
@Get('market-data/:symbol')
|
@Get('market-data/:symbol')
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
public async getMarketDataBySymbol(
|
public async getMarketDataBySymbol(
|
||||||
@Param('symbol') symbol
|
@Param('symbol') symbol: string
|
||||||
): Promise<AdminMarketDataDetails> {
|
): Promise<AdminMarketDataDetails> {
|
||||||
if (
|
if (
|
||||||
!hasPermission(
|
!hasPermission(
|
||||||
@ -190,6 +192,39 @@ export class AdminController {
|
|||||||
return this.adminService.getMarketDataBySymbol(symbol);
|
return this.adminService.getMarketDataBySymbol(symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Put('market-data/:dataSource/:symbol/:dateString')
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
|
public async update(
|
||||||
|
@Param('dataSource') dataSource: DataSource,
|
||||||
|
@Param('dateString') dateString: string,
|
||||||
|
@Param('symbol') symbol: string,
|
||||||
|
@Body() data: UpdateMarketDataDto
|
||||||
|
) {
|
||||||
|
if (
|
||||||
|
!hasPermission(
|
||||||
|
this.request.user.permissions,
|
||||||
|
permissions.accessAdminControl
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
throw new HttpException(
|
||||||
|
getReasonPhrase(StatusCodes.FORBIDDEN),
|
||||||
|
StatusCodes.FORBIDDEN
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const date = new Date(dateString);
|
||||||
|
|
||||||
|
return this.marketDataService.updateMarketData({
|
||||||
|
data,
|
||||||
|
where: {
|
||||||
|
date_symbol: {
|
||||||
|
date,
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Put('settings/:key')
|
@Put('settings/:key')
|
||||||
@UseGuards(AuthGuard('jwt'))
|
@UseGuards(AuthGuard('jwt'))
|
||||||
public async updateProperty(
|
public async updateProperty(
|
||||||
|
6
apps/api/src/app/admin/update-market-data.dto.ts
Normal file
6
apps/api/src/app/admin/update-market-data.dto.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { IsNumber } from 'class-validator';
|
||||||
|
|
||||||
|
export class UpdateMarketDataDto {
|
||||||
|
@IsNumber()
|
||||||
|
marketPrice: number;
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import type { RequestWithUser } from '@ghostfolio/common/types';
|
import type { RequestWithUser } from '@ghostfolio/common/types';
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
@ -12,9 +13,9 @@ import {
|
|||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { REQUEST } from '@nestjs/core';
|
import { REQUEST } from '@nestjs/core';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
import { DataSource } from '@prisma/client';
|
import { DataSource, MarketData } from '@prisma/client';
|
||||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||||
import { isEmpty } from 'lodash';
|
import { isDate, isEmpty } from 'lodash';
|
||||||
|
|
||||||
import { LookupItem } from './interfaces/lookup-item.interface';
|
import { LookupItem } from './interfaces/lookup-item.interface';
|
||||||
import { SymbolItem } from './interfaces/symbol-item.interface';
|
import { SymbolItem } from './interfaces/symbol-item.interface';
|
||||||
@ -78,4 +79,27 @@ export class SymbolController {
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get(':dataSource/:symbol/:dateString')
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
|
public async gatherSymbolForDate(
|
||||||
|
@Param('dataSource') dataSource: DataSource,
|
||||||
|
@Param('dateString') dateString: string,
|
||||||
|
@Param('symbol') symbol: string
|
||||||
|
): Promise<IDataProviderHistoricalResponse> {
|
||||||
|
const date = new Date(dateString);
|
||||||
|
|
||||||
|
if (!isDate(date)) {
|
||||||
|
throw new HttpException(
|
||||||
|
getReasonPhrase(StatusCodes.BAD_REQUEST),
|
||||||
|
StatusCodes.BAD_REQUEST
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.symbolService.getForDate({
|
||||||
|
dataSource,
|
||||||
|
date,
|
||||||
|
symbol
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
import { HistoricalDataItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
|
import { HistoricalDataItem } from '@ghostfolio/api/app/portfolio/interfaces/portfolio-position-detail.interface';
|
||||||
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
import { DataProviderService } from '@ghostfolio/api/services/data-provider/data-provider.service';
|
||||||
import { IDataGatheringItem } from '@ghostfolio/api/services/interfaces/interfaces';
|
import {
|
||||||
|
IDataGatheringItem,
|
||||||
|
IDataProviderHistoricalResponse
|
||||||
|
} from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
|
import { MarketDataService } from '@ghostfolio/api/services/market-data.service';
|
||||||
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
import { PrismaService } from '@ghostfolio/api/services/prisma.service';
|
||||||
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { DataSource } from '@prisma/client';
|
import { DataSource, MarketData } from '@prisma/client';
|
||||||
import { subDays } from 'date-fns';
|
import { format, subDays } from 'date-fns';
|
||||||
|
|
||||||
import { LookupItem } from './interfaces/lookup-item.interface';
|
import { LookupItem } from './interfaces/lookup-item.interface';
|
||||||
import { SymbolItem } from './interfaces/symbol-item.interface';
|
import { SymbolItem } from './interfaces/symbol-item.interface';
|
||||||
@ -58,6 +62,27 @@ export class SymbolService {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async getForDate({
|
||||||
|
dataSource,
|
||||||
|
date,
|
||||||
|
symbol
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
date: Date;
|
||||||
|
symbol: string;
|
||||||
|
}): Promise<IDataProviderHistoricalResponse> {
|
||||||
|
const historicalData = await this.dataProviderService.getHistoricalRaw(
|
||||||
|
[{ dataSource, symbol }],
|
||||||
|
date,
|
||||||
|
date
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
marketPrice:
|
||||||
|
historicalData?.[symbol]?.[format(date, DATE_FORMAT)]?.marketPrice
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public async lookup(aQuery: string): Promise<{ items: LookupItem[] }> {
|
public async lookup(aQuery: string): Promise<{ items: LookupItem[] }> {
|
||||||
const results: { items: LookupItem[] } = { items: [] };
|
const results: { items: LookupItem[] } = { items: [] };
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
|
|
||||||
import { YahooFinanceService } from './yahoo-finance.service';
|
import { YahooFinanceService } from './yahoo-finance.service';
|
||||||
|
|
||||||
jest.mock(
|
jest.mock(
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
import { LookupItem } from '@ghostfolio/api/app/symbol/interfaces/lookup-item.interface';
|
||||||
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
import { CryptocurrencyService } from '@ghostfolio/api/services/cryptocurrency/cryptocurrency.service';
|
||||||
import { baseCurrency, UNKNOWN_KEY } from '@ghostfolio/common/config';
|
import { UNKNOWN_KEY, baseCurrency } from '@ghostfolio/common/config';
|
||||||
import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT, isCurrency } from '@ghostfolio/common/helper';
|
||||||
import { Granularity } from '@ghostfolio/common/types';
|
import { Granularity } from '@ghostfolio/common/types';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
@ -65,4 +65,16 @@ export class MarketDataService {
|
|||||||
where
|
where
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async updateMarketData(params: {
|
||||||
|
data: Prisma.MarketDataUpdateInput;
|
||||||
|
where: Prisma.MarketDataWhereUniqueInput;
|
||||||
|
}): Promise<MarketData> {
|
||||||
|
const { data, where } = params;
|
||||||
|
|
||||||
|
return this.prismaService.marketData.update({
|
||||||
|
data,
|
||||||
|
where
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
Component,
|
Component,
|
||||||
|
EventEmitter,
|
||||||
Input,
|
Input,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnInit
|
OnInit,
|
||||||
|
Output
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
|
import { DEFAULT_DATE_FORMAT } from '@ghostfolio/common/config';
|
||||||
@ -27,6 +29,8 @@ export class AdminMarketDataDetailComponent implements OnChanges, OnInit {
|
|||||||
@Input() marketData: MarketData[];
|
@Input() marketData: MarketData[];
|
||||||
@Input() symbol: string;
|
@Input() symbol: string;
|
||||||
|
|
||||||
|
@Output() marketDataChanged = new EventEmitter<boolean>();
|
||||||
|
|
||||||
public days = Array(31);
|
public days = Array(31);
|
||||||
public defaultDateFormat = DEFAULT_DATE_FORMAT;
|
public defaultDateFormat = DEFAULT_DATE_FORMAT;
|
||||||
public deviceType: string;
|
public deviceType: string;
|
||||||
@ -101,7 +105,9 @@ export class AdminMarketDataDetailComponent implements OnChanges, OnInit {
|
|||||||
dialogRef
|
dialogRef
|
||||||
.afterClosed()
|
.afterClosed()
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe(() => {});
|
.subscribe(({ withRefresh }) => {
|
||||||
|
this.marketDataChanged.next(withRefresh);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||||
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
import { AdminService } from '@ghostfolio/client/services/admin.service';
|
||||||
import { MarketData } from '@prisma/client';
|
|
||||||
import { Subject, takeUntil } from 'rxjs';
|
import { Subject, takeUntil } from 'rxjs';
|
||||||
|
|
||||||
import { MarketDataDetailDialogParams } from './interfaces/interfaces';
|
import { MarketDataDetailDialogParams } from './interfaces/interfaces';
|
||||||
@ -32,24 +31,38 @@ export class MarketDataDetailDialog implements OnDestroy {
|
|||||||
public ngOnInit() {}
|
public ngOnInit() {}
|
||||||
|
|
||||||
public onCancel(): void {
|
public onCancel(): void {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close({ withRefresh: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
public onGatherData() {
|
public onFetchSymbolForDate() {
|
||||||
this.adminService
|
this.adminService
|
||||||
.gatherSymbol({
|
.fetchSymbolForDate({
|
||||||
dataSource: this.data.dataSource,
|
dataSource: this.data.dataSource,
|
||||||
date: this.data.date,
|
date: this.data.date,
|
||||||
symbol: this.data.symbol
|
symbol: this.data.symbol
|
||||||
})
|
})
|
||||||
.pipe(takeUntil(this.unsubscribeSubject))
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
.subscribe((marketData: MarketData) => {
|
.subscribe(({ marketPrice }) => {
|
||||||
this.data.marketPrice = marketData.marketPrice;
|
this.data.marketPrice = marketPrice;
|
||||||
|
|
||||||
this.changeDetectorRef.markForCheck();
|
this.changeDetectorRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onUpdate() {
|
||||||
|
this.adminService
|
||||||
|
.putMarketData({
|
||||||
|
dataSource: this.data.dataSource,
|
||||||
|
date: this.data.date,
|
||||||
|
marketData: { marketPrice: this.data.marketPrice },
|
||||||
|
symbol: this.data.symbol
|
||||||
|
})
|
||||||
|
.pipe(takeUntil(this.unsubscribeSubject))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.dialogRef.close({ withRefresh: true });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
this.unsubscribeSubject.next();
|
this.unsubscribeSubject.next();
|
||||||
this.unsubscribeSubject.complete();
|
this.unsubscribeSubject.complete();
|
||||||
|
@ -21,22 +21,30 @@
|
|||||||
<mat-datepicker #date disabled="true"></mat-datepicker>
|
<mat-datepicker #date disabled="true"></mat-datepicker>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div class="align-items-center d-flex">
|
<div>
|
||||||
<mat-form-field appearance="outline" class="flex-grow-1 mr-2">
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
<mat-label i18n>Market Price</mat-label>
|
<mat-label i18n>Market Price</mat-label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
name="marketPrice"
|
name="marketPrice"
|
||||||
readonly
|
type="number"
|
||||||
[(ngModel)]="data.marketPrice"
|
[(ngModel)]="data.marketPrice"
|
||||||
/>
|
/>
|
||||||
|
<button
|
||||||
|
mat-icon-button
|
||||||
|
matSuffix
|
||||||
|
title="Fetch market price"
|
||||||
|
(click)="onFetchSymbolForDate()"
|
||||||
|
>
|
||||||
|
<ion-icon class="text-muted" name="refresh-outline"></ion-icon>
|
||||||
|
</button>
|
||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
<button color="accent" i18n mat-flat-button (click)="onGatherData()">
|
|
||||||
Gather Data
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="justify-content-end" mat-dialog-actions>
|
<div class="justify-content-end" mat-dialog-actions>
|
||||||
<button i18n mat-button (click)="onCancel()">Cancel</button>
|
<button i18n mat-button (click)="onCancel()">Cancel</button>
|
||||||
|
<button color="primary" i18n mat-flat-button (click)="onUpdate()">
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -68,6 +68,13 @@ export class AdminMarketDataComponent implements OnDestroy, OnInit {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onMarketDataChanged(withRefresh: boolean = false) {
|
||||||
|
if (withRefresh) {
|
||||||
|
this.fetchAdminMarketData();
|
||||||
|
this.fetchAdminMarketDataBySymbol(this.currentSymbol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
this.unsubscribeSubject.next();
|
this.unsubscribeSubject.next();
|
||||||
this.unsubscribeSubject.complete();
|
this.unsubscribeSubject.complete();
|
||||||
|
@ -47,6 +47,7 @@
|
|||||||
[dataSource]="item.dataSource"
|
[dataSource]="item.dataSource"
|
||||||
[marketData]="marketDataDetails"
|
[marketData]="marketDataDetails"
|
||||||
[symbol]="item.symbol"
|
[symbol]="item.symbol"
|
||||||
|
(marketDataChanged)="onMarketDataChanged($event)"
|
||||||
></gf-admin-market-data-detail>
|
></gf-admin-market-data-detail>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -131,7 +131,7 @@
|
|||||||
</mat-form-field>
|
</mat-form-field>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<mat-form-field appearance="outline" class="unit-price w-100">
|
<mat-form-field appearance="outline" class="w-100">
|
||||||
<mat-label i18n>Unit Price</mat-label>
|
<mat-label i18n>Unit Price</mat-label>
|
||||||
<input
|
<input
|
||||||
matInput
|
matInput
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto';
|
||||||
|
import { IDataProviderHistoricalResponse } from '@ghostfolio/api/services/interfaces/interfaces';
|
||||||
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
import { DATE_FORMAT } from '@ghostfolio/common/helper';
|
||||||
import { DataSource, MarketData } from '@prisma/client';
|
import { DataSource, MarketData } from '@prisma/client';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
@ -35,4 +37,40 @@ export class AdminService {
|
|||||||
|
|
||||||
return this.http.post<MarketData | void>(url, {});
|
return this.http.post<MarketData | void>(url, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fetchSymbolForDate({
|
||||||
|
dataSource,
|
||||||
|
date,
|
||||||
|
symbol
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
date: Date;
|
||||||
|
symbol: string;
|
||||||
|
}) {
|
||||||
|
const url = `/api/symbol/${dataSource}/${symbol}/${format(
|
||||||
|
date,
|
||||||
|
DATE_FORMAT
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
return this.http.get<IDataProviderHistoricalResponse>(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
public putMarketData({
|
||||||
|
dataSource,
|
||||||
|
date,
|
||||||
|
marketData,
|
||||||
|
symbol
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
date: Date;
|
||||||
|
marketData: UpdateMarketDataDto;
|
||||||
|
symbol: string;
|
||||||
|
}) {
|
||||||
|
const url = `/api/admin/market-data/${dataSource}/${symbol}/${format(
|
||||||
|
date,
|
||||||
|
DATE_FORMAT
|
||||||
|
)}`;
|
||||||
|
|
||||||
|
return this.http.put<MarketData>(url, marketData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user