| 
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -8,6 +8,7 @@ import { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} from '@angular/core'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { FormBuilder, FormControl, Validators } from '@angular/forms'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { MatSnackBar } from '@angular/material/snack-bar'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { AdminService } from '@ghostfolio/client/services/admin.service'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { DataService } from '@ghostfolio/client/services/data.service'; | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -25,8 +26,8 @@ import { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					} from '@prisma/client'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { format } from 'date-fns'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { parse as csvToJson } from 'papaparse'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { Subject } from 'rxjs'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { takeUntil } from 'rxjs/operators'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { EMPTY, Subject } from 'rxjs'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { catchError, takeUntil } from 'rxjs/operators'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					import { AssetProfileDialogParams } from './interfaces/interfaces'; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -50,6 +51,9 @@ export class AssetProfileDialog implements OnDestroy, OnInit { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    assetClass: new FormControl<AssetClass>(undefined), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    assetSubClass: new FormControl<AssetSubClass>(undefined), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    comment: '', | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    historicalData: this.formBuilder.group({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      csvString: '' | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    }), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    name: ['', Validators.required], | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    scraperConfiguration: '', | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    symbolMapping: '' | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -59,7 +63,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public countries: { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    [code: string]: { name: string; value: number }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public historicalDataAsCsvString: string; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public isBenchmark = false; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public marketDataDetails: MarketData[] = []; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public sectors: { | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -78,7 +81,8 @@ export class AssetProfileDialog implements OnDestroy, OnInit { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    @Inject(MAT_DIALOG_DATA) public data: AssetProfileDialogParams, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    private dataService: DataService, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    public dialogRef: MatDialogRef<AssetProfileDialog>, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    private formBuilder: FormBuilder | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    private formBuilder: FormBuilder, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    private snackBar: MatSnackBar | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  ) {} | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public ngOnInit(): void { | 
				
			
			
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
				 | 
				
					@ -88,9 +92,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public initialize() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.historicalDataAsCsvString = | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      AssetProfileDialog.HISTORICAL_DATA_TEMPLATE; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.adminService | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      .fetchAdminMarketDataBySymbol({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        dataSource: this.data.dataSource, | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -131,6 +132,9 @@ export class AssetProfileDialog implements OnDestroy, OnInit { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          assetClass: this.assetProfile.assetClass ?? null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          assetSubClass: this.assetProfile.assetSubClass ?? null, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          comment: this.assetProfile?.comment ?? '', | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          historicalData: { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            csvString: AssetProfileDialog.HISTORICAL_DATA_TEMPLATE | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          name: this.assetProfile.name ?? this.assetProfile.symbol, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          scraperConfiguration: JSON.stringify( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            this.assetProfile?.scraperConfiguration ?? {} | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
				 | 
				
					@ -163,26 +167,46 @@ export class AssetProfileDialog implements OnDestroy, OnInit { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public onImportHistoricalData() { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    const marketData = csvToJson(this.historicalDataAsCsvString, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      dynamicTyping: true, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      header: true, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      skipEmptyLines: true | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    }).data; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    this.adminService | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      .postMarketData({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        dataSource: this.data.dataSource, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        marketData: { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          marketData: marketData.map(({ date, marketPrice }) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            return { marketPrice, date: parseDate(date).toISOString() }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        symbol: this.data.symbol | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      .pipe(takeUntil(this.unsubscribeSubject)) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      .subscribe(() => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.initialize(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    try { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      const marketData = csvToJson( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        this.assetProfileForm.controls['historicalData'].controls['csvString'] | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          .value, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          dynamicTyping: true, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          header: true, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          skipEmptyLines: true | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ).data; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.adminService | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .postMarketData({ | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          dataSource: this.data.dataSource, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          marketData: { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            marketData: marketData.map(({ date, marketPrice }) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              return { marketPrice, date: parseDate(date).toISOString() }; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            }) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          symbol: this.data.symbol | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .pipe( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          catchError(({ error, message }) => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            this.snackBar.open(`${error}: ${message[0]}`, undefined, { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					              duration: 3000 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					            return EMPTY; | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          }), | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          takeUntil(this.unsubscribeSubject) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        ) | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        .subscribe(() => { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					          this.initialize(); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        }); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } catch { | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      this.snackBar.open( | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        $localize`Oops! Could not parse historical data.`, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        undefined, | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					        { duration: 3000 } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					      ); | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					    } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  } | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					
 | 
				
			
			
		
	
		
			
				
					 | 
					 | 
				
				 | 
				
					  public onMarketDataChanged(withRefresh: boolean = false) { | 
				
			
			
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
				 | 
				
					
  |