diff --git a/CHANGELOG.md b/CHANGELOG.md
index aed13fe01..8e7a08306 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Added pagination to the users table of the admin control panel
 
+### Changed
+
+- Extracted the historical market data editor to a reusable component
+
 ## 2.125.0 - 2024-11-30
 
 ### Changed
diff --git a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.module.ts b/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.module.ts
deleted file mode 100644
index 9f4e1b3bc..000000000
--- a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.module.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import { GfLineChartComponent } from '@ghostfolio/ui/line-chart';
-
-import { CommonModule } from '@angular/common';
-import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
-
-import { AdminMarketDataDetailComponent } from './admin-market-data-detail.component';
-import { GfMarketDataDetailDialogModule } from './market-data-detail-dialog/market-data-detail-dialog.module';
-
-@NgModule({
-  declarations: [AdminMarketDataDetailComponent],
-  exports: [AdminMarketDataDetailComponent],
-  imports: [CommonModule, GfLineChartComponent, GfMarketDataDetailDialogModule],
-  schemas: [CUSTOM_ELEMENTS_SCHEMA]
-})
-export class GfAdminMarketDataDetailModule {}
diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts b/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts
deleted file mode 100644
index f3b55d71d..000000000
--- a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.module.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { CommonModule } from '@angular/common';
-import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';
-import { FormsModule, ReactiveFormsModule } from '@angular/forms';
-import { MatButtonModule } from '@angular/material/button';
-import { MatDatepickerModule } from '@angular/material/datepicker';
-import { MatDialogModule } from '@angular/material/dialog';
-import { MatFormFieldModule } from '@angular/material/form-field';
-import { MatInputModule } from '@angular/material/input';
-
-import { MarketDataDetailDialog } from './market-data-detail-dialog.component';
-
-@NgModule({
-  declarations: [MarketDataDetailDialog],
-  imports: [
-    CommonModule,
-    FormsModule,
-    MatButtonModule,
-    MatDatepickerModule,
-    MatDialogModule,
-    MatFormFieldModule,
-    MatInputModule,
-    ReactiveFormsModule
-  ],
-  schemas: [CUSTOM_ELEMENTS_SCHEMA]
-})
-export class GfMarketDataDetailDialogModule {}
diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss
index b63df0134..7057aad83 100644
--- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss
+++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.scss
@@ -3,5 +3,9 @@
 
   .mat-mdc-dialog-content {
     max-height: unset;
+
+    gf-line-chart {
+      aspect-ratio: 16/9;
+    }
   }
 }
diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
index aacf387e7..4fdc22986 100644
--- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
+++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.component.ts
@@ -1,15 +1,17 @@
 import { UpdateAssetProfileDto } from '@ghostfolio/api/app/admin/update-asset-profile.dto';
-import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto';
 import { AdminMarketDataService } from '@ghostfolio/client/components/admin-market-data/admin-market-data.service';
 import { NotificationService } from '@ghostfolio/client/core/notification/notification.service';
 import { AdminService } from '@ghostfolio/client/services/admin.service';
 import { DataService } from '@ghostfolio/client/services/data.service';
+import { UserService } from '@ghostfolio/client/services/user/user.service';
 import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
 import { ghostfolioScraperApiSymbolPrefix } from '@ghostfolio/common/config';
 import { DATE_FORMAT } from '@ghostfolio/common/helper';
 import {
   AdminMarketDataDetails,
-  AssetProfileIdentifier
+  AssetProfileIdentifier,
+  LineChartItem,
+  User
 } from '@ghostfolio/common/interfaces';
 import { translate } from '@ghostfolio/ui/i18n';
 
@@ -23,7 +25,6 @@ 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 {
   AssetClass,
   AssetSubClass,
@@ -31,7 +32,6 @@ import {
   SymbolProfile
 } from '@prisma/client';
 import { format } from 'date-fns';
-import { parse as csvToJson } from 'papaparse';
 import { EMPTY, Subject } from 'rxjs';
 import { catchError, takeUntil } from 'rxjs/operators';
 
@@ -75,11 +75,13 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
   };
   public currencies: string[] = [];
   public ghostfolioScraperApiSymbolPrefix = ghostfolioScraperApiSymbolPrefix;
+  public historicalDataItems: LineChartItem[];
   public isBenchmark = false;
-  public marketDataDetails: MarketData[] = [];
+  public marketDataItems: MarketData[] = [];
   public sectors: {
     [name: string]: { name: string; value: number };
   };
+  public user: User;
 
   private static readonly HISTORICAL_DATA_TEMPLATE = `date;marketPrice\n${format(
     new Date(),
@@ -96,7 +98,7 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
     public dialogRef: MatDialogRef<AssetProfileDialog>,
     private formBuilder: FormBuilder,
     private notificationService: NotificationService,
-    private snackBar: MatSnackBar
+    private userService: UserService
   ) {}
 
   public ngOnInit() {
@@ -109,6 +111,16 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
   }
 
   public initialize() {
+    this.historicalDataItems = undefined;
+
+    this.userService.stateChanged
+      .pipe(takeUntil(this.unsubscribeSubject))
+      .subscribe((state) => {
+        if (state?.user) {
+          this.user = state.user;
+        }
+      });
+
     this.adminService
       .fetchAdminMarketDataBySymbol({
         dataSource: this.data.dataSource,
@@ -121,10 +133,19 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
         this.assetProfileClass = translate(this.assetProfile?.assetClass);
         this.assetProfileSubClass = translate(this.assetProfile?.assetSubClass);
         this.countries = {};
+
         this.isBenchmark = this.benchmarks.some(({ id }) => {
           return id === this.assetProfile.id;
         });
-        this.marketDataDetails = marketData;
+
+        this.historicalDataItems = marketData.map(({ date, marketPrice }) => {
+          return {
+            date: format(date, DATE_FORMAT),
+            value: marketPrice
+          };
+        });
+
+        this.marketDataItems = marketData;
         this.sectors = {};
 
         if (this.assetProfile?.countries?.length > 0) {
@@ -200,47 +221,6 @@ export class AssetProfileDialog implements OnDestroy, OnInit {
       .subscribe();
   }
 
-  public onImportHistoricalData() {
-    try {
-      const marketData = csvToJson(
-        this.assetProfileForm.controls['historicalData'].controls['csvString']
-          .value,
-        {
-          dynamicTyping: true,
-          header: true,
-          skipEmptyLines: true
-        }
-      ).data as UpdateMarketDataDto[];
-
-      this.adminService
-        .postMarketData({
-          dataSource: this.data.dataSource,
-          marketData: {
-            marketData
-          },
-          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) {
     if (withRefresh) {
       this.initialize();
diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
index a5d2205d2..eeb43e932 100644
--- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
+++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.html
@@ -68,50 +68,28 @@
   </div>
 
   <div class="flex-grow-1" mat-dialog-content>
-    <gf-admin-market-data-detail
+    <gf-line-chart
+      class="mb-4"
+      [colorScheme]="user?.settings?.colorScheme"
+      [historicalDataItems]="historicalDataItems"
+      [isAnimated]="true"
+      [locale]="data.locale"
+      [showXAxis]="true"
+      [showYAxis]="true"
+      [symbol]="data.symbol"
+    />
+    <gf-historical-market-data-editor
       class="mb-3"
       [currency]="assetProfile?.currency"
       [dataSource]="data.dataSource"
       [dateOfFirstActivity]="assetProfile?.dateOfFirstActivity"
       [locale]="data.locale"
-      [marketData]="marketDataDetails"
+      [marketData]="marketDataItems"
       [symbol]="data.symbol"
+      [user]="user"
       (marketDataChanged)="onMarketDataChanged($event)"
     />
 
-    <div class="mt-3" formGroupName="historicalData">
-      <mat-form-field appearance="outline" class="w-100 without-hint">
-        <mat-label>
-          <ng-container i18n>Historical Data</ng-container> (CSV)
-        </mat-label>
-        <textarea
-          cdkAutosizeMaxRows="5"
-          cdkTextareaAutosize
-          formControlName="csvString"
-          matInput
-          type="text"
-          (keyup.enter)="$event.stopPropagation()"
-        ></textarea>
-      </mat-form-field>
-    </div>
-
-    <div class="d-flex justify-content-end mt-2">
-      <button
-        color="accent"
-        mat-flat-button
-        type="button"
-        [disabled]="
-          !assetProfileForm.controls['historicalData']?.controls['csvString']
-            .touched ||
-          assetProfileForm.controls['historicalData']?.controls['csvString']
-            ?.value === ''
-        "
-        (click)="onImportHistoricalData()"
-      >
-        <ng-container i18n>Import</ng-container>
-      </button>
-    </div>
-
     <div class="row">
       <div class="col-6 mb-3">
         <gf-value i18n size="medium" [value]="assetProfile?.symbol"
diff --git a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts
index a872f567f..d5e14ecb5 100644
--- a/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts
+++ b/apps/client/src/app/components/admin-market-data/asset-profile-dialog/asset-profile-dialog.module.ts
@@ -1,7 +1,8 @@
-import { GfAdminMarketDataDetailModule } from '@ghostfolio/client/components/admin-market-data-detail/admin-market-data-detail.module';
 import { AdminMarketDataService } from '@ghostfolio/client/components/admin-market-data/admin-market-data.service';
 import { GfAssetProfileIconComponent } from '@ghostfolio/client/components/asset-profile-icon/asset-profile-icon.component';
 import { GfCurrencySelectorComponent } from '@ghostfolio/ui/currency-selector';
+import { GfHistoricalMarketDataEditorComponent } from '@ghostfolio/ui/historical-market-data-editor';
+import { GfLineChartComponent } from '@ghostfolio/ui/line-chart';
 import { GfPortfolioProportionChartComponent } from '@ghostfolio/ui/portfolio-proportion-chart';
 import { GfValueComponent } from '@ghostfolio/ui/value';
 
@@ -24,9 +25,10 @@ import { AssetProfileDialog } from './asset-profile-dialog.component';
   imports: [
     CommonModule,
     FormsModule,
-    GfAdminMarketDataDetailModule,
     GfAssetProfileIconComponent,
     GfCurrencySelectorComponent,
+    GfHistoricalMarketDataEditorComponent,
+    GfLineChartComponent,
     GfPortfolioProportionChartComponent,
     GfValueComponent,
     MatButtonModule,
diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.component.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts
similarity index 60%
rename from apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.component.ts
rename to libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts
index 6a44d0dfb..434266e1e 100644
--- a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.component.ts
+++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.component.ts
@@ -1,34 +1,58 @@
 import { AdminService } from '@ghostfolio/client/services/admin.service';
 
+import { CommonModule } from '@angular/common';
 import {
   ChangeDetectionStrategy,
   ChangeDetectorRef,
   Component,
+  CUSTOM_ELEMENTS_SCHEMA,
   Inject,
   OnDestroy
 } from '@angular/core';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
 import { DateAdapter, MAT_DATE_LOCALE } from '@angular/material/core';
-import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
+import { MatDatepickerModule } from '@angular/material/datepicker';
+import {
+  MAT_DIALOG_DATA,
+  MatDialogModule,
+  MatDialogRef
+} from '@angular/material/dialog';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatInputModule } from '@angular/material/input';
 import { Subject, takeUntil } from 'rxjs';
 
-import { MarketDataDetailDialogParams } from './interfaces/interfaces';
+import { HistoricalMarketDataEditorDialogParams } from './interfaces/interfaces';
 
 @Component({
-  host: { class: 'h-100' },
-  selector: 'gf-market-data-detail-dialog',
   changeDetection: ChangeDetectionStrategy.OnPush,
-  styleUrls: ['./market-data-detail-dialog.scss'],
-  templateUrl: 'market-data-detail-dialog.html'
+  host: { class: 'h-100' },
+  imports: [
+    CommonModule,
+    FormsModule,
+    MatButtonModule,
+    MatDatepickerModule,
+    MatDialogModule,
+    MatFormFieldModule,
+    MatInputModule,
+    ReactiveFormsModule
+  ],
+  selector: 'gf-historical-market-data-editor-dialog',
+  schemas: [CUSTOM_ELEMENTS_SCHEMA],
+  standalone: true,
+  styleUrls: ['./historical-market-data-editor-dialog.scss'],
+  templateUrl: 'historical-market-data-editor-dialog.html'
 })
-export class MarketDataDetailDialog implements OnDestroy {
+export class GfHistoricalMarketDataEditorDialogComponent implements OnDestroy {
   private unsubscribeSubject = new Subject<void>();
 
   public constructor(
     private adminService: AdminService,
     private changeDetectorRef: ChangeDetectorRef,
-    @Inject(MAT_DIALOG_DATA) public data: MarketDataDetailDialogParams,
+    @Inject(MAT_DIALOG_DATA)
+    public data: HistoricalMarketDataEditorDialogParams,
     private dateAdapter: DateAdapter<any>,
-    public dialogRef: MatDialogRef<MarketDataDetailDialog>,
+    public dialogRef: MatDialogRef<GfHistoricalMarketDataEditorDialogComponent>,
     @Inject(MAT_DATE_LOCALE) private locale: string
   ) {}
 
diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html
similarity index 100%
rename from apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.html
rename to libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.html
diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.scss b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.scss
similarity index 100%
rename from apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/market-data-detail-dialog.scss
rename to libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/historical-market-data-editor-dialog.scss
diff --git a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/interfaces/interfaces.ts
similarity index 79%
rename from apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts
rename to libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/interfaces/interfaces.ts
index 81188cd1f..4248b3fdb 100644
--- a/apps/client/src/app/components/admin-market-data-detail/market-data-detail-dialog/interfaces/interfaces.ts
+++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor-dialog/interfaces/interfaces.ts
@@ -2,7 +2,7 @@ import { User } from '@ghostfolio/common/interfaces';
 
 import { DataSource } from '@prisma/client';
 
-export interface MarketDataDetailDialogParams {
+export interface HistoricalMarketDataEditorDialogParams {
   currency: string;
   dataSource: DataSource;
   dateString: string;
diff --git a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.html b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html
similarity index 51%
rename from apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.html
rename to libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html
index 617dd6962..b35e1d812 100644
--- a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.html
+++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.html
@@ -1,14 +1,4 @@
 <div>
-  <gf-line-chart
-    class="mb-4"
-    [colorScheme]="user?.settings?.colorScheme"
-    [historicalDataItems]="historicalDataItems"
-    [isAnimated]="true"
-    [locale]="locale"
-    [showXAxis]="true"
-    [showYAxis]="true"
-    [symbol]="symbol"
-  />
   @for (itemByMonth of marketDataByMonth | keyvalue; track itemByMonth) {
     <div class="d-flex">
       <div class="date px-1 text-nowrap">{{ itemByMonth.key }}</div>
@@ -43,4 +33,42 @@
       </div>
     </div>
   }
+  <form
+    class="d-flex flex-column h-100"
+    [formGroup]="historicalDataForm"
+    (ngSubmit)="onImportHistoricalData()"
+  >
+    <div class="mt-3" formGroupName="historicalData">
+      <mat-form-field appearance="outline" class="w-100 without-hint">
+        <mat-label>
+          <ng-container i18n>Historical Data</ng-container> (CSV)
+        </mat-label>
+        <textarea
+          cdkAutosizeMaxRows="5"
+          cdkTextareaAutosize
+          formControlName="csvString"
+          matInput
+          type="text"
+          (keyup.enter)="$event.stopPropagation()"
+        ></textarea>
+      </mat-form-field>
+    </div>
+
+    <div class="d-flex justify-content-end mt-2">
+      <button
+        color="accent"
+        mat-flat-button
+        type="button"
+        [disabled]="
+          !historicalDataForm.controls['historicalData']?.controls['csvString']
+            .touched ||
+          historicalDataForm.controls['historicalData']?.controls['csvString']
+            ?.value === ''
+        "
+        (click)="onImportHistoricalData()"
+      >
+        <ng-container i18n>Import</ng-container>
+      </button>
+    </div>
+  </form>
 </div>
diff --git a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.scss b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.scss
similarity index 90%
rename from apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.scss
rename to libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.scss
index a03533589..cc835a90e 100644
--- a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.scss
+++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.scss
@@ -2,10 +2,6 @@
   display: block;
   font-size: 0.9rem;
 
-  gf-line-chart {
-    aspect-ratio: 16/9;
-  }
-
   .date {
     font-feature-settings: 'tnum';
     font-variant-numeric: tabular-nums;
diff --git a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts
similarity index 55%
rename from apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts
rename to libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts
index 1742d8307..0fce78621 100644
--- a/apps/client/src/app/components/admin-market-data-detail/admin-market-data-detail.component.ts
+++ b/libs/ui/src/lib/historical-market-data-editor/historical-market-data-editor.component.ts
@@ -1,4 +1,5 @@
-import { UserService } from '@ghostfolio/client/services/user/user.service';
+import { UpdateMarketDataDto } from '@ghostfolio/api/app/admin/update-market-data.dto';
+import { AdminService } from '@ghostfolio/client/services/admin.service';
 import {
   DATE_FORMAT,
   getDateFormatString,
@@ -6,15 +7,22 @@ import {
 } from '@ghostfolio/common/helper';
 import { LineChartItem, User } from '@ghostfolio/common/interfaces';
 
+import { CommonModule } from '@angular/common';
 import {
   ChangeDetectionStrategy,
   Component,
   EventEmitter,
   Input,
   OnChanges,
+  OnDestroy,
+  OnInit,
   Output
 } from '@angular/core';
+import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
+import { MatButtonModule } from '@angular/material/button';
 import { MatDialog } from '@angular/material/dialog';
+import { MatInputModule } from '@angular/material/input';
+import { MatSnackBar } from '@angular/material/snack-bar';
 import { DataSource, MarketData } from '@prisma/client';
 import {
   addDays,
@@ -29,55 +37,70 @@ import {
   parseISO
 } from 'date-fns';
 import { first, last } from 'lodash';
+import ms from 'ms';
 import { DeviceDetectorService } from 'ngx-device-detector';
-import { Subject, takeUntil } from 'rxjs';
+import { parse as csvToJson } from 'papaparse';
+import { EMPTY, Subject, takeUntil } from 'rxjs';
+import { catchError } from 'rxjs/operators';
 
-import { MarketDataDetailDialogParams } from './market-data-detail-dialog/interfaces/interfaces';
-import { MarketDataDetailDialog } from './market-data-detail-dialog/market-data-detail-dialog.component';
+import { GfHistoricalMarketDataEditorDialogComponent } from './historical-market-data-editor-dialog/historical-market-data-editor-dialog.component';
+import { HistoricalMarketDataEditorDialogParams } from './historical-market-data-editor-dialog/interfaces/interfaces';
 
 @Component({
   changeDetection: ChangeDetectionStrategy.OnPush,
-  selector: 'gf-admin-market-data-detail',
-  styleUrls: ['./admin-market-data-detail.component.scss'],
-  templateUrl: './admin-market-data-detail.component.html'
+  imports: [CommonModule, MatButtonModule, MatInputModule, ReactiveFormsModule],
+  selector: 'gf-historical-market-data-editor',
+  standalone: true,
+  styleUrls: ['./historical-market-data-editor.component.scss'],
+  templateUrl: './historical-market-data-editor.component.html'
 })
-export class AdminMarketDataDetailComponent implements OnChanges {
+export class GfHistoricalMarketDataEditorComponent
+  implements OnChanges, OnDestroy, OnInit
+{
   @Input() currency: string;
   @Input() dataSource: DataSource;
   @Input() dateOfFirstActivity: string;
   @Input() locale = getLocale();
   @Input() marketData: MarketData[];
   @Input() symbol: string;
+  @Input() user: User;
 
   @Output() marketDataChanged = new EventEmitter<boolean>();
 
   public days = Array(31);
   public defaultDateFormat: string;
   public deviceType: string;
+  public historicalDataForm = this.formBuilder.group({
+    historicalData: this.formBuilder.group({
+      csvString: ''
+    })
+  });
   public historicalDataItems: LineChartItem[];
   public marketDataByMonth: {
     [yearMonth: string]: {
       [day: string]: Pick<MarketData, 'date' | 'marketPrice'> & { day: number };
     };
   } = {};
-  public user: User;
+
+  private static readonly HISTORICAL_DATA_TEMPLATE = `date;marketPrice\n${format(
+    new Date(),
+    DATE_FORMAT
+  )};123.45`;
 
   private unsubscribeSubject = new Subject<void>();
 
   public constructor(
+    private adminService: AdminService,
     private deviceService: DeviceDetectorService,
     private dialog: MatDialog,
-    private userService: UserService
+    private formBuilder: FormBuilder,
+    private snackBar: MatSnackBar
   ) {
     this.deviceType = this.deviceService.getDeviceInfo().deviceType;
+  }
 
-    this.userService.stateChanged
-      .pipe(takeUntil(this.unsubscribeSubject))
-      .subscribe((state) => {
-        if (state?.user) {
-          this.user = state.user;
-        }
-      });
+  public ngOnInit() {
+    this.initializeHistoricalDataForm();
   }
 
   public ngOnChanges() {
@@ -177,29 +200,84 @@ export class AdminMarketDataDetailComponent implements OnChanges {
   }) {
     const marketPrice = this.marketDataByMonth[yearMonth]?.[day]?.marketPrice;
 
-    const dialogRef = this.dialog.open(MarketDataDetailDialog, {
-      data: {
-        marketPrice,
-        currency: this.currency,
-        dataSource: this.dataSource,
-        dateString: `${yearMonth}-${day}`,
-        symbol: this.symbol,
-        user: this.user
-      } as MarketDataDetailDialogParams,
-      height: this.deviceType === 'mobile' ? '98vh' : '80vh',
-      width: this.deviceType === 'mobile' ? '100vw' : '50rem'
-    });
+    const dialogRef = this.dialog.open(
+      GfHistoricalMarketDataEditorDialogComponent,
+      {
+        data: {
+          marketPrice,
+          currency: this.currency,
+          dataSource: this.dataSource,
+          dateString: `${yearMonth}-${day}`,
+          symbol: this.symbol,
+          user: this.user
+        } as HistoricalMarketDataEditorDialogParams,
+        height: this.deviceType === 'mobile' ? '98vh' : '80vh',
+        width: this.deviceType === 'mobile' ? '100vw' : '50rem'
+      }
+    );
 
     dialogRef
       .afterClosed()
       .pipe(takeUntil(this.unsubscribeSubject))
       .subscribe(({ withRefresh } = { withRefresh: false }) => {
-        this.marketDataChanged.next(withRefresh);
+        this.marketDataChanged.emit(withRefresh);
       });
   }
 
+  public onImportHistoricalData() {
+    try {
+      const marketData = csvToJson(
+        this.historicalDataForm.controls['historicalData'].controls['csvString']
+          .value,
+        {
+          dynamicTyping: true,
+          header: true,
+          skipEmptyLines: true
+        }
+      ).data as UpdateMarketDataDto[];
+
+      this.adminService
+        .postMarketData({
+          dataSource: this.dataSource,
+          marketData: {
+            marketData
+          },
+          symbol: this.symbol
+        })
+        .pipe(
+          catchError(({ error, message }) => {
+            this.snackBar.open(`${error}: ${message[0]}`, undefined, {
+              duration: ms('3 seconds')
+            });
+            return EMPTY;
+          }),
+          takeUntil(this.unsubscribeSubject)
+        )
+        .subscribe(() => {
+          this.initializeHistoricalDataForm();
+
+          this.marketDataChanged.emit(true);
+        });
+    } catch {
+      this.snackBar.open(
+        $localize`Oops! Could not parse historical data.`,
+        undefined,
+        { duration: ms('3 seconds') }
+      );
+    }
+  }
+
   public ngOnDestroy() {
     this.unsubscribeSubject.next();
     this.unsubscribeSubject.complete();
   }
+
+  private initializeHistoricalDataForm() {
+    this.historicalDataForm.setValue({
+      historicalData: {
+        csvString:
+          GfHistoricalMarketDataEditorComponent.HISTORICAL_DATA_TEMPLATE
+      }
+    });
+  }
 }
diff --git a/libs/ui/src/lib/historical-market-data-editor/index.ts b/libs/ui/src/lib/historical-market-data-editor/index.ts
new file mode 100644
index 000000000..6c7004ce9
--- /dev/null
+++ b/libs/ui/src/lib/historical-market-data-editor/index.ts
@@ -0,0 +1 @@
+export * from './historical-market-data-editor.component';