mirror of https://github.com/ghostfolio/ghostfolio
committed by
GitHub
70 changed files with 5304 additions and 5549 deletions
@ -0,0 +1,208 @@ |
|||||
|
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; |
||||
|
import { |
||||
|
activityDummyData, |
||||
|
symbolProfileDummyData, |
||||
|
userDummyData |
||||
|
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; |
||||
|
import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; |
||||
|
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; |
||||
|
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; |
||||
|
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; |
||||
|
import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; |
||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; |
||||
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; |
||||
|
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; |
||||
|
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; |
||||
|
import { parseDate } from '@ghostfolio/common/helper'; |
||||
|
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; |
||||
|
|
||||
|
import { Big } from 'big.js'; |
||||
|
|
||||
|
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { |
||||
|
return { |
||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
CurrentRateService: jest.fn().mockImplementation(() => { |
||||
|
return CurrentRateServiceMock; |
||||
|
}) |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
jest.mock( |
||||
|
'@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', |
||||
|
() => { |
||||
|
return { |
||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
PortfolioSnapshotService: jest.fn().mockImplementation(() => { |
||||
|
return PortfolioSnapshotServiceMock; |
||||
|
}) |
||||
|
}; |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { |
||||
|
return { |
||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
RedisCacheService: jest.fn().mockImplementation(() => { |
||||
|
return RedisCacheServiceMock; |
||||
|
}) |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
describe('PortfolioCalculator', () => { |
||||
|
let configurationService: ConfigurationService; |
||||
|
let currentRateService: CurrentRateService; |
||||
|
let exchangeRateDataService: ExchangeRateDataService; |
||||
|
let portfolioCalculatorFactory: PortfolioCalculatorFactory; |
||||
|
let portfolioSnapshotService: PortfolioSnapshotService; |
||||
|
let redisCacheService: RedisCacheService; |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
configurationService = new ConfigurationService(); |
||||
|
|
||||
|
currentRateService = new CurrentRateService(null, null, null, null); |
||||
|
|
||||
|
exchangeRateDataService = new ExchangeRateDataService( |
||||
|
null, |
||||
|
null, |
||||
|
null, |
||||
|
null |
||||
|
); |
||||
|
|
||||
|
portfolioSnapshotService = new PortfolioSnapshotService(null); |
||||
|
|
||||
|
redisCacheService = new RedisCacheService(null, null); |
||||
|
|
||||
|
portfolioCalculatorFactory = new PortfolioCalculatorFactory( |
||||
|
configurationService, |
||||
|
currentRateService, |
||||
|
exchangeRateDataService, |
||||
|
portfolioSnapshotService, |
||||
|
redisCacheService |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
describe('get current positions', () => { |
||||
|
it.only('with BALN.SW buy and buy', async () => { |
||||
|
jest.useFakeTimers().setSystemTime(parseDate('2021-12-18').getTime()); |
||||
|
|
||||
|
const activities: Activity[] = [ |
||||
|
{ |
||||
|
...activityDummyData, |
||||
|
date: new Date('2021-11-22'), |
||||
|
feeInAssetProfileCurrency: 1.55, |
||||
|
quantity: 2, |
||||
|
SymbolProfile: { |
||||
|
...symbolProfileDummyData, |
||||
|
currency: 'CHF', |
||||
|
dataSource: 'YAHOO', |
||||
|
name: 'Bâloise Holding AG', |
||||
|
symbol: 'BALN.SW' |
||||
|
}, |
||||
|
type: 'BUY', |
||||
|
unitPriceInAssetProfileCurrency: 142.9 |
||||
|
}, |
||||
|
{ |
||||
|
...activityDummyData, |
||||
|
date: new Date('2021-11-30'), |
||||
|
feeInAssetProfileCurrency: 1.65, |
||||
|
quantity: 2, |
||||
|
SymbolProfile: { |
||||
|
...symbolProfileDummyData, |
||||
|
currency: 'CHF', |
||||
|
dataSource: 'YAHOO', |
||||
|
name: 'Bâloise Holding AG', |
||||
|
symbol: 'BALN.SW' |
||||
|
}, |
||||
|
type: 'BUY', |
||||
|
unitPriceInAssetProfileCurrency: 136.6 |
||||
|
} |
||||
|
]; |
||||
|
|
||||
|
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ |
||||
|
activities, |
||||
|
calculationType: PerformanceCalculationType.ROAI, |
||||
|
currency: 'CHF', |
||||
|
userId: userDummyData.id |
||||
|
}); |
||||
|
|
||||
|
const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); |
||||
|
|
||||
|
const investments = portfolioCalculator.getInvestments(); |
||||
|
|
||||
|
const investmentsByMonth = portfolioCalculator.getInvestmentsByGroup({ |
||||
|
data: portfolioSnapshot.historicalData, |
||||
|
groupBy: 'month' |
||||
|
}); |
||||
|
|
||||
|
expect(portfolioSnapshot).toMatchObject({ |
||||
|
currentValueInBaseCurrency: new Big('595.6'), |
||||
|
errors: [], |
||||
|
hasErrors: false, |
||||
|
positions: [ |
||||
|
{ |
||||
|
averagePrice: new Big('139.75'), |
||||
|
currency: 'CHF', |
||||
|
dataSource: 'YAHOO', |
||||
|
dividend: new Big('0'), |
||||
|
dividendInBaseCurrency: new Big('0'), |
||||
|
fee: new Big('3.2'), |
||||
|
feeInBaseCurrency: new Big('3.2'), |
||||
|
firstBuyDate: '2021-11-22', |
||||
|
grossPerformance: new Big('36.6'), |
||||
|
grossPerformancePercentage: new Big('0.07706261539956593567'), |
||||
|
grossPerformancePercentageWithCurrencyEffect: new Big( |
||||
|
'0.07706261539956593567' |
||||
|
), |
||||
|
grossPerformanceWithCurrencyEffect: new Big('36.6'), |
||||
|
investment: new Big('559'), |
||||
|
investmentWithCurrencyEffect: new Big('559'), |
||||
|
netPerformance: new Big('33.4'), |
||||
|
netPerformancePercentage: new Big('0.07032490039195361342'), |
||||
|
netPerformancePercentageWithCurrencyEffectMap: { |
||||
|
max: new Big('0.06986689805847808234') |
||||
|
}, |
||||
|
netPerformanceWithCurrencyEffectMap: { |
||||
|
max: new Big('33.4') |
||||
|
}, |
||||
|
marketPrice: 148.9, |
||||
|
marketPriceInBaseCurrency: 148.9, |
||||
|
quantity: new Big('4'), |
||||
|
symbol: 'BALN.SW', |
||||
|
tags: [], |
||||
|
timeWeightedInvestment: new Big('474.93846153846153846154'), |
||||
|
timeWeightedInvestmentWithCurrencyEffect: new Big( |
||||
|
'474.93846153846153846154' |
||||
|
), |
||||
|
transactionCount: 2, |
||||
|
valueInBaseCurrency: new Big('595.6') |
||||
|
} |
||||
|
], |
||||
|
totalFeesWithCurrencyEffect: new Big('3.2'), |
||||
|
totalInterestWithCurrencyEffect: new Big('0'), |
||||
|
totalInvestment: new Big('559'), |
||||
|
totalInvestmentWithCurrencyEffect: new Big('559'), |
||||
|
totalLiabilitiesWithCurrencyEffect: new Big('0') |
||||
|
}); |
||||
|
|
||||
|
expect(portfolioSnapshot.historicalData.at(-1)).toMatchObject( |
||||
|
expect.objectContaining({ |
||||
|
netPerformance: 33.4, |
||||
|
netPerformanceInPercentage: 0.07032490039195362, |
||||
|
netPerformanceInPercentageWithCurrencyEffect: 0.07032490039195362, |
||||
|
netPerformanceWithCurrencyEffect: 33.4, |
||||
|
totalInvestmentValueWithCurrencyEffect: 559 |
||||
|
}) |
||||
|
); |
||||
|
|
||||
|
expect(investments).toEqual([ |
||||
|
{ date: '2021-11-22', investment: new Big('285.8') }, |
||||
|
{ date: '2021-11-30', investment: new Big('559') } |
||||
|
]); |
||||
|
|
||||
|
expect(investmentsByMonth).toEqual([ |
||||
|
{ date: '2021-11-01', investment: 559 }, |
||||
|
{ date: '2021-12-01', investment: 0 } |
||||
|
]); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,132 @@ |
|||||
|
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto'; |
||||
|
import { Activity } from '@ghostfolio/api/app/order/interfaces/activities.interface'; |
||||
|
import { |
||||
|
activityDummyData, |
||||
|
loadActivityExportFile, |
||||
|
symbolProfileDummyData, |
||||
|
userDummyData |
||||
|
} from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator-test-utils'; |
||||
|
import { PortfolioCalculatorFactory } from '@ghostfolio/api/app/portfolio/calculator/portfolio-calculator.factory'; |
||||
|
import { CurrentRateService } from '@ghostfolio/api/app/portfolio/current-rate.service'; |
||||
|
import { CurrentRateServiceMock } from '@ghostfolio/api/app/portfolio/current-rate.service.mock'; |
||||
|
import { RedisCacheService } from '@ghostfolio/api/app/redis-cache/redis-cache.service'; |
||||
|
import { RedisCacheServiceMock } from '@ghostfolio/api/app/redis-cache/redis-cache.service.mock'; |
||||
|
import { ConfigurationService } from '@ghostfolio/api/services/configuration/configuration.service'; |
||||
|
import { ExchangeRateDataService } from '@ghostfolio/api/services/exchange-rate-data/exchange-rate-data.service'; |
||||
|
import { PortfolioSnapshotService } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service'; |
||||
|
import { PortfolioSnapshotServiceMock } from '@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service.mock'; |
||||
|
import { parseDate } from '@ghostfolio/common/helper'; |
||||
|
import { PerformanceCalculationType } from '@ghostfolio/common/types/performance-calculation-type.type'; |
||||
|
|
||||
|
import { Tag } from '@prisma/client'; |
||||
|
import { Big } from 'big.js'; |
||||
|
import { join } from 'path'; |
||||
|
|
||||
|
jest.mock('@ghostfolio/api/app/portfolio/current-rate.service', () => { |
||||
|
return { |
||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
CurrentRateService: jest.fn().mockImplementation(() => { |
||||
|
return CurrentRateServiceMock; |
||||
|
}) |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
jest.mock( |
||||
|
'@ghostfolio/api/services/queues/portfolio-snapshot/portfolio-snapshot.service', |
||||
|
() => { |
||||
|
return { |
||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
PortfolioSnapshotService: jest.fn().mockImplementation(() => { |
||||
|
return PortfolioSnapshotServiceMock; |
||||
|
}) |
||||
|
}; |
||||
|
} |
||||
|
); |
||||
|
|
||||
|
jest.mock('@ghostfolio/api/app/redis-cache/redis-cache.service', () => { |
||||
|
return { |
||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||
|
RedisCacheService: jest.fn().mockImplementation(() => { |
||||
|
return RedisCacheServiceMock; |
||||
|
}) |
||||
|
}; |
||||
|
}); |
||||
|
|
||||
|
describe('PortfolioCalculator', () => { |
||||
|
let activityDtos: CreateOrderDto[]; |
||||
|
|
||||
|
let configurationService: ConfigurationService; |
||||
|
let currentRateService: CurrentRateService; |
||||
|
let exchangeRateDataService: ExchangeRateDataService; |
||||
|
let portfolioCalculatorFactory: PortfolioCalculatorFactory; |
||||
|
let portfolioSnapshotService: PortfolioSnapshotService; |
||||
|
let redisCacheService: RedisCacheService; |
||||
|
|
||||
|
beforeAll(() => { |
||||
|
activityDtos = loadActivityExportFile( |
||||
|
join(__dirname, '../../../../../../../test/import/ok/btcusd-short.json') |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
configurationService = new ConfigurationService(); |
||||
|
|
||||
|
currentRateService = new CurrentRateService(null, null, null, null); |
||||
|
|
||||
|
exchangeRateDataService = new ExchangeRateDataService( |
||||
|
null, |
||||
|
null, |
||||
|
null, |
||||
|
null |
||||
|
); |
||||
|
|
||||
|
portfolioSnapshotService = new PortfolioSnapshotService(null); |
||||
|
|
||||
|
redisCacheService = new RedisCacheService(null, null); |
||||
|
|
||||
|
portfolioCalculatorFactory = new PortfolioCalculatorFactory( |
||||
|
configurationService, |
||||
|
currentRateService, |
||||
|
exchangeRateDataService, |
||||
|
portfolioSnapshotService, |
||||
|
redisCacheService |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
describe('get current positions', () => { |
||||
|
it.only('with BTCUSD short sell (in USD)', async () => { |
||||
|
jest.useFakeTimers().setSystemTime(parseDate('2022-01-14').getTime()); |
||||
|
|
||||
|
const activities: Activity[] = activityDtos.map((activity) => ({ |
||||
|
...activityDummyData, |
||||
|
...activity, |
||||
|
date: parseDate(activity.date), |
||||
|
feeInAssetProfileCurrency: activity.fee, |
||||
|
SymbolProfile: { |
||||
|
...symbolProfileDummyData, |
||||
|
currency: 'USD', |
||||
|
dataSource: activity.dataSource, |
||||
|
name: 'Bitcoin', |
||||
|
symbol: activity.symbol |
||||
|
}, |
||||
|
tags: activity.tags?.map((id) => { |
||||
|
return { id } as Tag; |
||||
|
}), |
||||
|
unitPriceInAssetProfileCurrency: activity.unitPrice |
||||
|
})); |
||||
|
|
||||
|
const portfolioCalculator = portfolioCalculatorFactory.createCalculator({ |
||||
|
activities, |
||||
|
calculationType: PerformanceCalculationType.ROAI, |
||||
|
currency: 'USD', |
||||
|
userId: userDummyData.id |
||||
|
}); |
||||
|
|
||||
|
const portfolioSnapshot = await portfolioCalculator.computeSnapshot(); |
||||
|
|
||||
|
expect(portfolioSnapshot.positions[0].averagePrice).toEqual( |
||||
|
Big(45647.95) |
||||
|
); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -1,38 +0,0 @@ |
|||||
import { GfDialogFooterComponent } from '@ghostfolio/client/components/dialog-footer/dialog-footer.component'; |
|
||||
import { GfDialogHeaderComponent } from '@ghostfolio/client/components/dialog-header/dialog-header.component'; |
|
||||
import { GfInvestmentChartModule } from '@ghostfolio/client/components/investment-chart/investment-chart.module'; |
|
||||
import { GfAccountBalancesComponent } from '@ghostfolio/ui/account-balances'; |
|
||||
import { GfActivitiesTableComponent } from '@ghostfolio/ui/activities-table'; |
|
||||
import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table'; |
|
||||
import { GfValueComponent } from '@ghostfolio/ui/value'; |
|
||||
|
|
||||
import { CommonModule } from '@angular/common'; |
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
|
||||
import { MatButtonModule } from '@angular/material/button'; |
|
||||
import { MatDialogModule } from '@angular/material/dialog'; |
|
||||
import { MatTabsModule } from '@angular/material/tabs'; |
|
||||
import { IonIcon } from '@ionic/angular/standalone'; |
|
||||
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader'; |
|
||||
|
|
||||
import { AccountDetailDialog } from './account-detail-dialog.component'; |
|
||||
|
|
||||
@NgModule({ |
|
||||
declarations: [AccountDetailDialog], |
|
||||
imports: [ |
|
||||
CommonModule, |
|
||||
GfAccountBalancesComponent, |
|
||||
GfActivitiesTableComponent, |
|
||||
GfDialogFooterComponent, |
|
||||
GfDialogHeaderComponent, |
|
||||
GfHoldingsTableComponent, |
|
||||
GfInvestmentChartModule, |
|
||||
GfValueComponent, |
|
||||
IonIcon, |
|
||||
MatButtonModule, |
|
||||
MatDialogModule, |
|
||||
MatTabsModule, |
|
||||
NgxSkeletonLoaderModule |
|
||||
], |
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
|
||||
}) |
|
||||
export class GfAccountDetailDialogModule {} |
|
||||
@ -1,5 +1,7 @@ |
|||||
@if (deviceType === 'mobile') { |
@if (deviceType === 'mobile') { |
||||
|
<div class="d-flex justify-content-center" mat-dialog-actions> |
||||
<button mat-button (click)="onClickCloseButton()"> |
<button mat-button (click)="onClickCloseButton()"> |
||||
<ion-icon name="close" size="large" /> |
<ion-icon name="close" size="large" /> |
||||
</button> |
</button> |
||||
|
</div> |
||||
} |
} |
||||
|
|||||
@ -1,9 +1,3 @@ |
|||||
:host { |
:host { |
||||
display: flex; |
display: block; |
||||
flex: 0 0 auto; |
|
||||
min-height: 0; |
|
||||
|
|
||||
@media (min-width: 576px) { |
|
||||
padding: 0 !important; |
|
||||
} |
|
||||
} |
} |
||||
|
|||||
@ -1,4 +1,3 @@ |
|||||
:host { |
:host { |
||||
align-items: center; |
display: block; |
||||
display: flex; |
|
||||
} |
} |
||||
|
|||||
@ -1,15 +0,0 @@ |
|||||
import { GfValueComponent } from '@ghostfolio/ui/value'; |
|
||||
|
|
||||
import { CommonModule } from '@angular/common'; |
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
|
||||
import { MatTooltipModule } from '@angular/material/tooltip'; |
|
||||
|
|
||||
import { PortfolioSummaryComponent } from './portfolio-summary.component'; |
|
||||
|
|
||||
@NgModule({ |
|
||||
declarations: [PortfolioSummaryComponent], |
|
||||
exports: [PortfolioSummaryComponent], |
|
||||
imports: [CommonModule, GfValueComponent, MatTooltipModule], |
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
|
||||
}) |
|
||||
export class GfPortfolioSummaryModule {} |
|
||||
@ -1,25 +0,0 @@ |
|||||
import { CommonModule } from '@angular/common'; |
|
||||
import { NgModule } from '@angular/core'; |
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms'; |
|
||||
import { MatButtonModule } from '@angular/material/button'; |
|
||||
import { MatDialogModule } from '@angular/material/dialog'; |
|
||||
import { MatFormFieldModule } from '@angular/material/form-field'; |
|
||||
import { MatInputModule } from '@angular/material/input'; |
|
||||
import { MatSelectModule } from '@angular/material/select'; |
|
||||
|
|
||||
import { CreateOrUpdateAccessDialog } from './create-or-update-access-dialog.component'; |
|
||||
|
|
||||
@NgModule({ |
|
||||
declarations: [CreateOrUpdateAccessDialog], |
|
||||
imports: [ |
|
||||
CommonModule, |
|
||||
FormsModule, |
|
||||
MatButtonModule, |
|
||||
MatDialogModule, |
|
||||
MatFormFieldModule, |
|
||||
MatInputModule, |
|
||||
MatSelectModule, |
|
||||
ReactiveFormsModule |
|
||||
] |
|
||||
}) |
|
||||
export class GfCreateOrUpdateAccessDialogModule {} |
|
||||
@ -1,20 +0,0 @@ |
|||||
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; |
|
||||
|
|
||||
import { NgModule } from '@angular/core'; |
|
||||
import { RouterModule, Routes } from '@angular/router'; |
|
||||
|
|
||||
import { PublicPageComponent } from './public-page.component'; |
|
||||
|
|
||||
const routes: Routes = [ |
|
||||
{ |
|
||||
canActivate: [AuthGuard], |
|
||||
component: PublicPageComponent, |
|
||||
path: ':id' |
|
||||
} |
|
||||
]; |
|
||||
|
|
||||
@NgModule({ |
|
||||
imports: [RouterModule.forChild(routes)], |
|
||||
exports: [RouterModule] |
|
||||
}) |
|
||||
export class PublicPageRoutingModule {} |
|
||||
@ -1,28 +0,0 @@ |
|||||
import { GfWorldMapChartComponent } from '@ghostfolio/client/components/world-map-chart/world-map-chart.component'; |
|
||||
import { GfHoldingsTableComponent } from '@ghostfolio/ui/holdings-table'; |
|
||||
import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart'; |
|
||||
import { GfValueComponent } from '@ghostfolio/ui/value'; |
|
||||
|
|
||||
import { CommonModule } from '@angular/common'; |
|
||||
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; |
|
||||
import { MatButtonModule } from '@angular/material/button'; |
|
||||
import { MatCardModule } from '@angular/material/card'; |
|
||||
|
|
||||
import { PublicPageRoutingModule } from './public-page-routing.module'; |
|
||||
import { PublicPageComponent } from './public-page.component'; |
|
||||
|
|
||||
@NgModule({ |
|
||||
declarations: [PublicPageComponent], |
|
||||
imports: [ |
|
||||
CommonModule, |
|
||||
GfHoldingsTableComponent, |
|
||||
GfPortfolioProportionChartComponent, |
|
||||
GfValueComponent, |
|
||||
GfWorldMapChartComponent, |
|
||||
MatButtonModule, |
|
||||
MatCardModule, |
|
||||
PublicPageRoutingModule |
|
||||
], |
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA] |
|
||||
}) |
|
||||
export class PublicPageModule {} |
|
||||
@ -0,0 +1,13 @@ |
|||||
|
import { AuthGuard } from '@ghostfolio/client/core/auth.guard'; |
||||
|
|
||||
|
import { Routes } from '@angular/router'; |
||||
|
|
||||
|
import { GfPublicPageComponent } from './public-page.component'; |
||||
|
|
||||
|
export const routes: Routes = [ |
||||
|
{ |
||||
|
canActivate: [AuthGuard], |
||||
|
component: GfPublicPageComponent, |
||||
|
path: ':id' |
||||
|
} |
||||
|
]; |
||||
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,42 @@ |
|||||
|
{ |
||||
|
"meta": { |
||||
|
"date": "2021-12-12T00:00:00.000Z", |
||||
|
"version": "dev" |
||||
|
}, |
||||
|
"accounts": [], |
||||
|
"platforms": [], |
||||
|
"tags": [], |
||||
|
"activities": [ |
||||
|
{ |
||||
|
"accountId": null, |
||||
|
"comment": null, |
||||
|
"fee": 4.46, |
||||
|
"quantity": 1, |
||||
|
"type": "SELL", |
||||
|
"unitPrice": 44558.42, |
||||
|
"currency": "USD", |
||||
|
"dataSource": "YAHOO", |
||||
|
"date": "2021-12-12T00:00:00.000Z", |
||||
|
"symbol": "BTCUSD", |
||||
|
"tags": [] |
||||
|
}, |
||||
|
{ |
||||
|
"accountId": null, |
||||
|
"comment": null, |
||||
|
"fee": 4.46, |
||||
|
"quantity": 1, |
||||
|
"type": "SELL", |
||||
|
"unitPrice": 46737.48, |
||||
|
"currency": "USD", |
||||
|
"dataSource": "YAHOO", |
||||
|
"date": "2021-12-13T00:00:00.000Z", |
||||
|
"symbol": "BTCUSD", |
||||
|
"tags": [] |
||||
|
} |
||||
|
], |
||||
|
"user": { |
||||
|
"settings": { |
||||
|
"currency": "USD" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue