Загрузка данных


diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6210266202a47fde78482776dbd48a66325e4ae2..79efc44dca9c2b91e56e9e02dac959f520e565e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # latest
 
+- Фикс контейнера дровингов. Теперь их контейнер не содержит `priceScale`
+- Рефактор дровингов
+- Фикс названий дровингов связанных с объёмом
+- Исправлено поведение дровинга `fixed range profile volume` 
+- `NaN` получающийся в результате деления на ноль при расчете отосительной цены заменён на `0`
+- К форматированию чисел добавлена обработка бесконечностей 
 - Исправлена обработка событий клика в модальных окнах
 
 # 0.1.13
diff --git a/src/components/Toolbar/constants.tsx b/src/components/Toolbar/constants.tsx
index dc7a9d5df6d0edcd56e5b31cb199d42a90b36cf8..8e64384c4633234dc0a552dd3c59dea0cd09ee64 100644
--- a/src/components/Toolbar/constants.tsx
+++ b/src/components/Toolbar/constants.tsx
@@ -30,11 +30,11 @@ export const measurementTools = (): MenuOption<DrawingsNames>[] => [
   { value: DrawingsNames.sliderShort, icon: <SlidersHorizontalShortDashedIcon />, label: t('Short position') },
   { value: DrawingsNames.diapsonDates, icon: <DiapsonDatesIcon />, label: t('Dates range') },
   { value: DrawingsNames.diapsonPrices, icon: <DiapsonPricesIcon />, label: t('Prices range') },
-  { value: DrawingsNames.fixedRangeProfile, icon: <FixedProfileIcon />, label: t('Fixed profile volume') },
+  { value: DrawingsNames.fixedRangeProfile, icon: <FixedProfileIcon />, label: t('Fixed range profile volume') },
   {
     value: DrawingsNames.visibleRangeProfile,
     icon: <VisibleRangeProfileIcon />,
-    label: t('Visible profile volume range'),
+    label: t('Anchored volume profile'),
   },
 ];
 
diff --git a/src/constants/drawing.ts b/src/constants/drawing.ts
index 741bce86f6652085f51763281a0498a49e66e3dd..dc6b47343c76ca7aa28e07c38b60b4c5fa8e2be4 100644
--- a/src/constants/drawing.ts
+++ b/src/constants/drawing.ts
@@ -47,8 +47,8 @@ export const drawingLabelById = (): Record<DrawingsNames, string> => ({
   [DrawingsNames.sliderShort]: t('Short position'),
   [DrawingsNames.diapsonDates]: t('Dates range'),
   [DrawingsNames.diapsonPrices]: t('Prices range'),
-  [DrawingsNames.fixedRangeProfile]: t('Fixed profile volume'),
-  [DrawingsNames.visibleRangeProfile]: t('Visible profile volume range'),
+  [DrawingsNames.fixedRangeProfile]: t('Fixed range profile volume'),
+  [DrawingsNames.visibleRangeProfile]: t('Anchored volume profile'),
   [DrawingsNames.rectangle]: t('Rectangle'),
   [DrawingsNames.traectory]: t('Traectory'),
   [DrawingsNames.text]: t('Text'),
diff --git a/src/core/Drawings/axisLine/axisLine.ts b/src/core/Drawings/axisLine/axisLine.ts
index 74faefd59ebd9273a6518816e913eb05a29833c5..7d789c2fa7f4734fae7b9a91ad24b6f31a7a4d12 100644
--- a/src/core/Drawings/axisLine/axisLine.ts
+++ b/src/core/Drawings/axisLine/axisLine.ts
@@ -69,8 +69,6 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
 
   protected settings: AxisLineSettings = createDefaultSettings();
 
-  private isBound = false;
-
   protected mode: AxisLineMode = 'idle';
 
   private direction: AxisLineDirection;
@@ -251,7 +249,7 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
     };
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (kind !== 'main' || this.direction !== 'vertical' || !this.isActive.value || this.time === null) {
       return null;
     }
@@ -277,7 +275,7 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (kind !== 'main' || this.direction !== 'horizontal' || !this.isActive.value || this.price === null) {
       return null;
     }
@@ -298,11 +296,11 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  protected getTimeAxisSegments(): AxisSegment[] {
     return [];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     return [];
   }
 
@@ -310,35 +308,7 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
     console.log('stub');
   }
 
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'ready') {
       return;
     }
@@ -368,7 +338,7 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
     this.openSettings?.();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -429,7 +399,7 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
     this.render();
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId) {
       return;
     }
@@ -441,7 +411,7 @@ export class AxisLine extends SeriesDrawingBase<AxisLineSettings> implements ISe
     this.render();
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/common.ts b/src/core/Drawings/common.ts
index 42dfa937937637257735319b92e5cb5272e44868..17b684c44213d139162207869f8b8ae1c85b493e 100644
--- a/src/core/Drawings/common.ts
+++ b/src/core/Drawings/common.ts
@@ -39,6 +39,8 @@ export interface ISeriesDrawing extends ISeriesPrimitive<Time> {
   getSettingsTabs(): SettingsTab[];
 
   subscribeIsSelected(cb: (isSelected: boolean) => void): void;
+
+  getRenderData(): unknown;
 }
 
 interface SeriesDrawingBaseParams {
@@ -52,10 +54,11 @@ export abstract class SeriesDrawingBase<TSettings extends SettingsValues = Setti
   protected chart: IChartApi;
   protected series: SeriesApi;
   protected subscriptions = new Subscription();
-  protected abstract mode: unknown;
+  protected abstract mode: unknown; // todo: хочется иметь единый mode
   protected abstract settings: TSettings;
   protected readonly container: HTMLElement;
   protected isActive: BehaviorSubject<boolean> = new BehaviorSubject(false);
+  protected isBound = false;
 
   protected readyPromise: null | Promise<void> = null;
   protected resolveReady: null | (() => void) = null;
@@ -153,6 +156,20 @@ export abstract class SeriesDrawingBase<TSettings extends SettingsValues = Setti
     return null;
   }
 
+  public abstract getRenderData(): unknown; // todo: make proper type
+  public abstract getState(): unknown;
+  public abstract getSettingsTabs(): SettingsTab[];
+  public abstract isCreationPending(): boolean;
+  public abstract setState(state: unknown): void;
+
+  public abstract hitTest(x: number, y: number): PrimitiveHoveredItem | null;
+  public abstract updateAllViews(): void;
+  public abstract paneViews(): readonly IPrimitivePaneView[];
+  public abstract priceAxisPaneViews(): readonly IPrimitivePaneView[];
+  public abstract priceAxisViews(): readonly ISeriesPrimitiveAxisView[];
+  public abstract timeAxisPaneViews(): readonly IPrimitivePaneView[];
+  public abstract timeAxisViews(): readonly ISeriesPrimitiveAxisView[];
+
   protected render(): void {
     this.updateAllViews();
     this.requestUpdate?.();
@@ -178,25 +195,48 @@ export abstract class SeriesDrawingBase<TSettings extends SettingsValues = Setti
     return getPointerPointFromEvent(this.container, event);
   }
 
-  public abstract getRenderData(): any; // todo: make proper type  // todo: добавить в интерфейс\точно ли паблик
-  public abstract hitTest(x: number, y: number): PrimitiveHoveredItem | null; // todo: добавить в интерфейс\точно ли паблик
-  public abstract getTimeAxisSegments(): AxisSegment[]; // todo: добавить в интерфейс\точно ли паблик
-  public abstract getPriceAxisSegments(): AxisSegment[]; // todo: добавить в интерфейс\точно ли паблик
-  public abstract getTimeAxisLabel(kind: string): AxisLabel | null; // todo: добавить в интерфейс\точно ли паблик
-  public abstract getPriceAxisLabel(kind: string): AxisLabel | null; // todo: добавить в интерфейс\точно ли паблик
-  protected abstract bindEvents(): void;
-  public abstract updateAllViews(): void;
-  protected abstract unbindEvents(): void;
-  protected abstract getGeometry(): any; // todo: make proper type // todo: добавить в интерфейс\точно ли паблик
+  protected bindEvents(): void {
+    if (this.isBound) {
+      return;
+    }
 
-  public abstract getSettingsTabs(): SettingsTab[];
-  public abstract getState(): unknown;
-  public abstract isCreationPending(): boolean;
+    this.isBound = true;
 
-  public abstract paneViews(): readonly IPrimitivePaneView[];
-  public abstract priceAxisPaneViews(): readonly IPrimitivePaneView[];
-  public abstract priceAxisViews(): readonly ISeriesPrimitiveAxisView[];
-  public abstract setState(state: unknown): void;
-  public abstract timeAxisPaneViews(): readonly IPrimitivePaneView[];
-  public abstract timeAxisViews(): readonly ISeriesPrimitiveAxisView[];
+    this.container.addEventListener('dblclick', this.handleDoubleClick);
+    this.container.addEventListener('pointerdown', this.handlePointerDown);
+    this.container.addEventListener('contextmenu', this.handleContextMenu);
+
+    window.addEventListener('pointermove', this.handlePointerMove);
+    window.addEventListener('pointerup', this.handlePointerUp);
+    window.addEventListener('pointercancel', this.handlePointerUp);
+  }
+
+  protected unbindEvents(): void {
+    if (!this.isBound) {
+      return;
+    }
+
+    this.isBound = false;
+
+    this.container.removeEventListener('dblclick', this.handleDoubleClick);
+    this.container.removeEventListener('pointerdown', this.handlePointerDown);
+    this.container.removeEventListener('contextmenu', this.handleContextMenu);
+
+    window.removeEventListener('pointermove', this.handlePointerMove);
+    window.removeEventListener('pointerup', this.handlePointerUp);
+    window.removeEventListener('pointercancel', this.handlePointerUp);
+  }
+
+  // todo: хочется общую реализацию для каждой кнопки
+  protected handleContextMenu(event: MouseEvent): void {}
+  protected handleDoubleClick(event: MouseEvent): void {}
+  protected handlePointerDown(event: PointerEvent): void {}
+  protected handlePointerMove(event: PointerEvent): void {}
+  protected handlePointerUp(event: PointerEvent): void {}
+
+  protected abstract getGeometry(): unknown; // todo: make proper type
+  protected abstract getTimeAxisSegments(): AxisSegment[];
+  protected abstract getPriceAxisSegments(): AxisSegment[];
+  protected abstract getTimeAxisLabel(kind: string): AxisLabel | null;
+  protected abstract getPriceAxisLabel(kind: string): AxisLabel | null;
 }
diff --git a/src/core/Drawings/diapson/diapson.ts b/src/core/Drawings/diapson/diapson.ts
index b6f80a2c7f2c950773dfedc12d748c728ac979c7..023a81eefadca2d32b4f3faae1d89ee63ad3a88b 100644
--- a/src/core/Drawings/diapson/diapson.ts
+++ b/src/core/Drawings/diapson/diapson.ts
@@ -110,8 +110,6 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
 
   protected settings: DiapsonSettings = createDefaultSettings();
 
-  private isBound = false;
-
   protected mode: DiapsonMode = 'idle';
   private rangeMode: DiapsonRangeMode;
 
@@ -307,7 +305,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  protected getTimeAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -329,7 +327,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -351,7 +349,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     ];
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'left' && kind !== 'right')) {
       return null;
     }
@@ -374,7 +372,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'top' && kind !== 'bottom')) {
       return null;
     }
@@ -437,35 +435,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     };
   }
 
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'ready') {
       return;
     }
@@ -482,7 +452,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     this.openSettings?.();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -537,7 +507,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     this.startDragging(point, event.pointerId, dragTarget);
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     const point = this.getEventPoint(event);
 
     if (this.mode === 'drawing') {
@@ -561,7 +531,7 @@ export class Diapson extends SeriesDrawingBase<DiapsonSettings> implements ISeri
     this.render();
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts b/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts
index 3b2223fc372812c73ce91276c55cc6035cb3c163..023f081cf26bfd8420cd5fe334803818705fdd3a 100644
--- a/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts
+++ b/src/core/Drawings/fibonacciRetracement/fibonacciRetracement.ts
@@ -117,8 +117,6 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
 
   protected settings: FibonacciRetracementSettings = createDefaultSettings();
 
-  private isBound = false;
-
   protected mode: FibonacciRetracementMode = 'idle';
 
   private startTime: Time | null = null;
@@ -304,7 +302,39 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
+    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
+      return null;
+    }
+
+    const point = { x, y };
+
+    if (!this.isActive.value && !this.containsPoint(point)) {
+      return null;
+    }
+
+    const handleTarget = this.isActive.value ? this.getHandleTarget(point) : null;
+
+    if (handleTarget) {
+      return {
+        cursorStyle: 'pointer',
+        externalId: 'fibonacci-retracement-position',
+        zOrder: 'top',
+      };
+    }
+
+    if (!this.containsPoint(point)) {
+      return null;
+    }
+
+    return {
+      cursorStyle: this.isActive.value ? 'grab' : 'pointer',
+      externalId: 'fibonacci-retracement-position',
+      zOrder: 'top',
+    };
+  }
+
+  protected getTimeAxisSegments(): AxisSegment[] {
     const bounds = this.isActive.value ? this.getTimeBounds() : null;
 
     if (!bounds) {
@@ -322,7 +352,7 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     const bounds = this.isActive.value ? this.getPriceBounds() : null;
 
     if (!bounds) {
@@ -340,7 +370,7 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     ];
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     const coordinate = this.isActive.value ? this.getTimeCoordinate(kind as TimeLabelKind) : null;
     const text = this.getTimeText(kind as TimeLabelKind);
 
@@ -358,7 +388,7 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     const coordinate = this.isActive.value ? this.getPriceCoordinate(kind as PriceLabelKind) : null;
     const text = this.getPriceText(kind as PriceLabelKind);
 
@@ -376,67 +406,7 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     };
   }
 
-  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
-    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
-      return null;
-    }
-
-    const point = { x, y };
-
-    if (!this.isActive.value && !this.containsPoint(point)) {
-      return null;
-    }
-
-    const handleTarget = this.isActive.value ? this.getHandleTarget(point) : null;
-
-    if (handleTarget) {
-      return {
-        cursorStyle: 'pointer',
-        externalId: 'fibonacci-retracement-position',
-        zOrder: 'top',
-      };
-    }
-
-    if (!this.containsPoint(point)) {
-      return null;
-    }
-
-    return {
-      cursorStyle: this.isActive.value ? 'grab' : 'pointer',
-      externalId: 'fibonacci-retracement-position',
-      zOrder: 'top',
-    };
-  }
-
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     const point = this.getMousePoint(event);
 
     if (this.hidden || this.mode !== 'ready' || (!this.containsPoint(point) && !this.getHandleTarget(point))) {
@@ -449,7 +419,7 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     this.openSettings?.();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -497,7 +467,7 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     this.startDragging(point, event.pointerId, dragTarget);
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     const point = this.getEventPoint(event);
 
     if (this.mode === 'drawing') {
@@ -520,7 +490,7 @@ export class FibonacciRetracement extends SeriesDrawingBase<FibonacciRetracement
     this.render();
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/ray/ray.ts b/src/core/Drawings/ray/ray.ts
index 6427435c8cebfbccd866ffed64d6a35284b0e30d..7c1c0a8395e8d8b340805253eb78b7c962475495 100644
--- a/src/core/Drawings/ray/ray.ts
+++ b/src/core/Drawings/ray/ray.ts
@@ -75,8 +75,6 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
 
   protected settings: RaySettings = createDefaultSettings();
 
-  private isBound = false;
-
   protected mode: RayMode = 'idle';
 
   private startAnchor: Anchor | null = null;
@@ -279,7 +277,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  protected getTimeAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -301,7 +299,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -323,7 +321,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     ];
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'start' && kind !== 'direction')) {
       return null;
     }
@@ -345,7 +343,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'start' && kind !== 'direction')) {
       return null;
     }
@@ -367,35 +365,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     };
   }
 
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'ready') {
       return;
     }
@@ -416,7 +386,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     this.openSettings?.();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -487,7 +457,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     this.render();
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     const point = this.getEventPoint(event);
 
     if (this.mode === 'drawing') {
@@ -517,7 +487,7 @@ export class Ray extends SeriesDrawingBase<RaySettings> implements ISeriesDrawin
     }
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/rectangle/rectangle.ts b/src/core/Drawings/rectangle/rectangle.ts
index bba8fe1bdb78da2bd9eaf35034fa55934c405f01..117c65b941707f5ecddc0928f04401ae12670563 100644
--- a/src/core/Drawings/rectangle/rectangle.ts
+++ b/src/core/Drawings/rectangle/rectangle.ts
@@ -103,8 +103,6 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
 
   protected settings: RectangleSettings = createDefaultSettings();
 
-  private isBound = false;
-
   protected mode: RectangleMode = 'idle';
 
   private startTime: Time | null = null;
@@ -296,7 +294,47 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
+    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
+      return null;
+    }
+
+    const point = { x, y };
+
+    if (!this.isActive.value) {
+      if (!this.containsPoint(point)) {
+        return null;
+      }
+
+      return {
+        cursorStyle: 'pointer',
+        externalId: 'rectangle-position',
+        zOrder: 'top',
+      };
+    }
+
+    const handleTarget = this.getHandleTarget(point);
+
+    if (handleTarget) {
+      return {
+        cursorStyle: this.getCursorStyle(handleTarget),
+        externalId: 'rectangle-position',
+        zOrder: 'top',
+      };
+    }
+
+    if (!this.containsPoint(point)) {
+      return null;
+    }
+
+    return {
+      cursorStyle: 'grab',
+      externalId: 'rectangle-position',
+      zOrder: 'top',
+    };
+  }
+
+  protected getTimeAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -318,7 +356,7 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -340,7 +378,7 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     ];
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'left' && kind !== 'right')) {
       return null;
     }
@@ -363,7 +401,7 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'top' && kind !== 'bottom')) {
       return null;
     }
@@ -386,75 +424,7 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     };
   }
 
-  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
-    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
-      return null;
-    }
-
-    const point = { x, y };
-
-    if (!this.isActive.value) {
-      if (!this.containsPoint(point)) {
-        return null;
-      }
-
-      return {
-        cursorStyle: 'pointer',
-        externalId: 'rectangle-position',
-        zOrder: 'top',
-      };
-    }
-
-    const handleTarget = this.getHandleTarget(point);
-
-    if (handleTarget) {
-      return {
-        cursorStyle: this.getCursorStyle(handleTarget),
-        externalId: 'rectangle-position',
-        zOrder: 'top',
-      };
-    }
-
-    if (!this.containsPoint(point)) {
-      return null;
-    }
-
-    return {
-      cursorStyle: 'grab',
-      externalId: 'rectangle-position',
-      zOrder: 'top',
-    };
-  }
-
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'ready') {
       return;
     }
@@ -475,7 +445,7 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     this.openSettings?.();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -530,7 +500,7 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     this.startDragging(point, event.pointerId, dragTarget);
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     const point = this.getEventPoint(event);
 
     if (this.mode === 'drawing') {
@@ -554,7 +524,7 @@ export class Rectangle extends SeriesDrawingBase<RectangleSettings> implements I
     this.render();
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/ruler/ruler.ts b/src/core/Drawings/ruler/ruler.ts
index 29e152426714b958064d2a9c15ff3333ecad52f6..d4f92979645291b56738c7b4f9f3041b2b3a4c71 100644
--- a/src/core/Drawings/ruler/ruler.ts
+++ b/src/core/Drawings/ruler/ruler.ts
@@ -86,8 +86,6 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
   private readonly clickHandler: MouseEventHandler<Time>;
   private readonly moveHandler: MouseEventHandler<Time>;
 
-  private isBound = false;
-
   private readonly paneView: RulerPaneView;
   private readonly timeAxisPaneView: CustomTimeAxisPaneView;
   private readonly priceAxisPaneView: CustomPriceAxisPaneView;
@@ -377,7 +375,11 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
     return formatPrice(Number(anchor.price)) ?? '';
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
+    return null;
+  }
+
+  protected getTimeAxisSegments(): AxisSegment[] {
     const bounds = this.getTimeBounds();
 
     if (!bounds) {
@@ -395,7 +397,7 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     const bounds = this.getPriceBounds();
 
     if (!bounds) {
@@ -413,7 +415,7 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
     ];
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (kind !== 'start' && kind !== 'end') {
       return null;
     }
@@ -435,7 +437,7 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (kind !== 'start' && kind !== 'end') {
       return null;
     }
@@ -457,10 +459,6 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
     };
   }
 
-  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
-    return null;
-  }
-
   private findIndexByTime(time: Time): number {
     const data = this.series.data() ?? [];
 
@@ -474,6 +472,7 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
   }
 
   protected bindEvents(): void {
+    // todo: попробовать привести к виду базового класса
     if (this.isBound) {
       return;
     }
@@ -488,6 +487,7 @@ export class Ruler extends SeriesDrawingBase implements ISeriesDrawing {
   }
 
   protected unbindEvents(): void {
+    // todo: попробовать привести к виду базового класса
     if (!this.isBound) {
       return;
     }
diff --git a/src/core/Drawings/sliderPosition/sliderPosition.ts b/src/core/Drawings/sliderPosition/sliderPosition.ts
index 43153825fe028c751d07a1ee38de32114595d986..d7bc6dd594a1d7e57810081a57a509f37d9ae9da 100644
--- a/src/core/Drawings/sliderPosition/sliderPosition.ts
+++ b/src/core/Drawings/sliderPosition/sliderPosition.ts
@@ -143,7 +143,6 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
   private tickSize = 1;
 
   private ignoreNextChartClick = false;
-  private isBound = false;
 
   private readonly clickHandler: MouseEventHandler<Time>;
   private readonly paneView: SliderPaneView;
@@ -362,111 +361,6 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
-    if (!this.isActive.value) {
-      return [];
-    }
-
-    const bounds = this.getTimeBounds();
-
-    if (!bounds) {
-      return [];
-    }
-
-    const { colors } = getThemeStore();
-
-    return [
-      {
-        from: bounds.left,
-        to: bounds.right,
-        color: colors.axisMarkerAreaFill,
-      },
-    ];
-  }
-
-  public getPriceAxisSegments(): AxisSegment[] {
-    if (!this.isActive.value) {
-      return [];
-    }
-
-    const geometry = this.getGeometry();
-
-    if (!geometry) {
-      return [];
-    }
-
-    const { colors } = getThemeStore();
-
-    return [
-      {
-        from: geometry.profitTop,
-        to: geometry.profitBottom,
-        color: colors.axisMarkerAreaFill,
-      },
-      {
-        from: geometry.lossTop,
-        to: geometry.lossBottom,
-        color: colors.axisMarkerAreaFill,
-      },
-    ];
-  }
-
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
-    if (!this.isActive.value || (kind !== 'start' && kind !== 'end')) {
-      return null;
-    }
-
-    const labelKind = kind as TimeLabelKind;
-    const coordinate = this.getTimeCoordinate(labelKind);
-    const text = this.getTimeText(labelKind);
-
-    if (coordinate === null || !text) {
-      return null;
-    }
-
-    const { colors } = getThemeStore();
-
-    return {
-      coordinate,
-      text,
-      textColor: colors.chartPriceLineText,
-      backgroundColor: colors.axisMarkerLabelFill,
-    };
-  }
-
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
-    if (kind !== 'target' && kind !== 'entry' && kind !== 'stop') {
-      return null;
-    }
-
-    const labelKind = kind as PriceLabelKind;
-    const coordinate = this.getPriceCoordinate(labelKind);
-    const text = this.getPriceText(labelKind);
-
-    if (coordinate === null || !text) {
-      return null;
-    }
-
-    const { colors } = getThemeStore();
-
-    let backgroundColor = colors.axisMarkerLabelDefaultFill;
-
-    if (labelKind === 'target') {
-      backgroundColor = colors.axisMarkerLabelPositiveFill;
-    }
-
-    if (labelKind === 'stop') {
-      backgroundColor = colors.axisMarkerLabelNegativeFill;
-    }
-
-    return {
-      coordinate,
-      text,
-      textColor: colors.chartPriceLineText,
-      backgroundColor,
-    };
-  }
-
   public getTimeBounds(): { left: number; right: number } | null {
     const start = this.getTimeCoordinate('start');
     const end = this.getTimeCoordinate('end');
@@ -583,7 +477,113 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
     };
   }
 
+  protected getTimeAxisSegments(): AxisSegment[] {
+    if (!this.isActive.value) {
+      return [];
+    }
+
+    const bounds = this.getTimeBounds();
+
+    if (!bounds) {
+      return [];
+    }
+
+    const { colors } = getThemeStore();
+
+    return [
+      {
+        from: bounds.left,
+        to: bounds.right,
+        color: colors.axisMarkerAreaFill,
+      },
+    ];
+  }
+
+  protected getPriceAxisSegments(): AxisSegment[] {
+    if (!this.isActive.value) {
+      return [];
+    }
+
+    const geometry = this.getGeometry();
+
+    if (!geometry) {
+      return [];
+    }
+
+    const { colors } = getThemeStore();
+
+    return [
+      {
+        from: geometry.profitTop,
+        to: geometry.profitBottom,
+        color: colors.axisMarkerAreaFill,
+      },
+      {
+        from: geometry.lossTop,
+        to: geometry.lossBottom,
+        color: colors.axisMarkerAreaFill,
+      },
+    ];
+  }
+
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
+    if (!this.isActive.value || (kind !== 'start' && kind !== 'end')) {
+      return null;
+    }
+
+    const labelKind = kind as TimeLabelKind;
+    const coordinate = this.getTimeCoordinate(labelKind);
+    const text = this.getTimeText(labelKind);
+
+    if (coordinate === null || !text) {
+      return null;
+    }
+
+    const { colors } = getThemeStore();
+
+    return {
+      coordinate,
+      text,
+      textColor: colors.chartPriceLineText,
+      backgroundColor: colors.axisMarkerLabelFill,
+    };
+  }
+
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
+    if (kind !== 'target' && kind !== 'entry' && kind !== 'stop') {
+      return null;
+    }
+
+    const labelKind = kind as PriceLabelKind;
+    const coordinate = this.getPriceCoordinate(labelKind);
+    const text = this.getPriceText(labelKind);
+
+    if (coordinate === null || !text) {
+      return null;
+    }
+
+    const { colors } = getThemeStore();
+
+    let backgroundColor = colors.axisMarkerLabelDefaultFill;
+
+    if (labelKind === 'target') {
+      backgroundColor = colors.axisMarkerLabelPositiveFill;
+    }
+
+    if (labelKind === 'stop') {
+      backgroundColor = colors.axisMarkerLabelNegativeFill;
+    }
+
+    return {
+      coordinate,
+      text,
+      textColor: colors.chartPriceLineText,
+      backgroundColor,
+    };
+  }
+
   protected bindEvents(): void {
+    // todo: почему то отличается от базового класса
     if (this.isBound) {
       return;
     }
@@ -600,6 +600,7 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
   }
 
   protected unbindEvents(): void {
+    // todo: почему то отличается от базового класса
     if (!this.isBound) {
       return;
     }
@@ -615,7 +616,7 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
     window.removeEventListener('pointercancel', this.handlePointerUp);
   }
 
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'ready' || !this.isActive.value) {
       return;
     }
@@ -675,7 +676,7 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
     this.render();
   }
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || this.mode !== 'ready' || !this.isActive.value) {
       return;
     }
@@ -698,7 +699,7 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
     this.mode = 'dragging';
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId || !this.dragStateSnapshot) {
       return;
     }
@@ -710,7 +711,7 @@ export class SliderPosition extends SeriesDrawingBase<SliderPositionSettings> im
     this.render();
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/text/text.ts b/src/core/Drawings/text/text.ts
index 3711206afe3a5b53a03dbc961ea1273503e5d8a5..428dad133c9af41a194c4ab50c0111f3a195ede6 100644
--- a/src/core/Drawings/text/text.ts
+++ b/src/core/Drawings/text/text.ts
@@ -74,8 +74,6 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
   private readonly removeSelf?: () => void;
   private readonly openSettings?: () => void;
 
-  private isBound = false;
-
   protected mode: TextMode = 'idle';
 
   private displayFormat: ChartOptionsModel = {
@@ -210,7 +208,23 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
     };
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
+    if (this.hidden || this.mode === 'idle') {
+      return null;
+    }
+
+    if (!this.containsPoint({ x, y })) {
+      return null;
+    }
+
+    return {
+      cursorStyle: 'move',
+      externalId: 'text',
+      zOrder: 'top',
+    };
+  }
+
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (kind !== 'main' || !this.isActive.value || !this.point || typeof this.point.time !== 'number') {
       return null;
     }
@@ -236,7 +250,7 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (kind !== 'main' || !this.isActive.value || !this.point) {
       return null;
     }
@@ -257,59 +271,15 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
     };
   }
 
-  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
-    if (this.hidden || this.mode === 'idle') {
-      return null;
-    }
-
-    if (!this.containsPoint({ x, y })) {
-      return null;
-    }
-
-    return {
-      cursorStyle: 'move',
-      externalId: 'text',
-      zOrder: 'top',
-    };
-  }
-
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     return [];
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  protected getTimeAxisSegments(): AxisSegment[] {
     return [];
   }
 
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -355,7 +325,7 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
     this.render();
   };
 
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'ready' || !this.isActive.value) {
       return;
     }
@@ -372,7 +342,7 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
     this.openSettings?.();
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     if (this.dragPointerId !== event.pointerId || this.mode !== 'dragging') {
       return;
     }
@@ -383,7 +353,7 @@ export class Text extends SeriesDrawingBase<TextSettings> implements ISeriesDraw
     this.render();
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/traectory/traectory.ts b/src/core/Drawings/traectory/traectory.ts
index 968f22a6d2fe1ac013be3462d5235008996e5a98..fd458aeeea80b0d2d32d344945e6473e621a115c 100644
--- a/src/core/Drawings/traectory/traectory.ts
+++ b/src/core/Drawings/traectory/traectory.ts
@@ -66,8 +66,6 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
 
   protected settings: TraectorySettings = createDefaultSettings();
 
-  private isBound = false;
-
   protected mode: TraectoryMode = 'idle';
 
   private points: Anchor[] = [];
@@ -189,7 +187,34 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
+    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
+      return null;
+    }
+
+    const point = { x, y };
+    const pointIndex = this.getPointIndexAt(point);
+
+    if (pointIndex !== null) {
+      return {
+        cursorStyle: 'move',
+        externalId: 'traectory',
+        zOrder: 'top',
+      };
+    }
+
+    if (!this.isPointNearTraectory(point)) {
+      return null;
+    }
+
+    return {
+      cursorStyle: 'move',
+      externalId: 'traectory',
+      zOrder: 'top',
+    };
+  }
+
+  protected getTimeAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -211,7 +236,7 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -233,72 +258,15 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
     ];
   }
 
-  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
-    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
-      return null;
-    }
-
-    const point = { x, y };
-    const pointIndex = this.getPointIndexAt(point);
-
-    if (pointIndex !== null) {
-      return {
-        cursorStyle: 'move',
-        externalId: 'traectory',
-        zOrder: 'top',
-      };
-    }
-
-    if (!this.isPointNearTraectory(point)) {
-      return null;
-    }
-
-    return {
-      cursorStyle: 'move',
-      externalId: 'traectory',
-      zOrder: 'top',
-    };
-  }
-
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     return null;
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     return null;
   }
 
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('contextmenu', this.handleContextMenu);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('contextmenu', this.handleContextMenu);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden) {
       return;
     }
@@ -330,7 +298,7 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
     this.openSettings?.();
   };
 
-  private handleContextMenu = (event: MouseEvent): void => {
+  protected handleContextMenu = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'drawing') {
       return;
     }
@@ -341,7 +309,7 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
     this.finishDrawing();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -408,7 +376,7 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
     this.startDraggingBody(point, event.pointerId);
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     const point = this.getEventPoint(event);
 
     if (this.mode === 'drawing') {
@@ -434,7 +402,7 @@ export class Traectory extends SeriesDrawingBase<TraectorySettings> implements I
     }
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/trendLine/trendLine.ts b/src/core/Drawings/trendLine/trendLine.ts
index 514e804fa62d28824ac015e1bf837445d1e9a93b..fc3663cda2fb3288905259aac0bb21e567ee6bff 100644
--- a/src/core/Drawings/trendLine/trendLine.ts
+++ b/src/core/Drawings/trendLine/trendLine.ts
@@ -79,8 +79,6 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
 
   protected settings: TrendLineSettings = createDefaultSettings();
 
-  private isBound = false;
-
   protected mode: TrendLineMode = 'idle';
 
   private startAnchor: Anchor | null = null;
@@ -283,7 +281,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  protected getTimeAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -305,7 +303,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     if (!this.isActive.value) {
       return [];
     }
@@ -327,7 +325,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     ];
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'start' && kind !== 'end')) {
       return null;
     }
@@ -349,7 +347,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (!this.isActive.value || (kind !== 'start' && kind !== 'end')) {
       return null;
     }
@@ -371,35 +369,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     };
   }
 
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
       return;
     }
@@ -420,7 +390,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     this.openSettings?.();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -491,7 +461,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     this.render();
   };
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     const point = this.getEventPoint(event);
 
     if (this.mode === 'drawing') {
@@ -521,7 +491,7 @@ export class TrendLine extends SeriesDrawingBase<TrendLineSettings> implements I
     }
   };
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.dragPointerId !== event.pointerId) {
       return;
     }
diff --git a/src/core/Drawings/volumeProfile/volumeProfile.ts b/src/core/Drawings/volumeProfile/volumeProfile.ts
index 3b4ce5137c2a2c8b9a2d3d29dfd50f86756c88ac..d17554e8b2389dd1897869f4b3afd3b60e1d1316 100644
--- a/src/core/Drawings/volumeProfile/volumeProfile.ts
+++ b/src/core/Drawings/volumeProfile/volumeProfile.ts
@@ -123,7 +123,6 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
   private readonly profileKind: VolumeProfileKind;
   protected settings: VolumeProfileSettings = createDefaultSettings();
 
-  private isBound = false;
   protected mode: VolumeProfileMode = 'idle';
 
   private startAnchor: Anchor | null = null;
@@ -364,7 +363,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     };
   }
 
-  public getTimeAxisSegments(): AxisSegment[] {
+  protected getTimeAxisSegments(): AxisSegment[] {
     if (this.profileKind === 'visibleRange' || !this.isActive.value) {
       return [];
     }
@@ -386,7 +385,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     ];
   }
 
-  public getPriceAxisSegments(): AxisSegment[] {
+  protected getPriceAxisSegments(): AxisSegment[] {
     if (this.profileKind === 'visibleRange' || !this.isActive.value) {
       return [];
     }
@@ -408,7 +407,33 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     ];
   }
 
-  public getTimeAxisLabel(kind: string): AxisLabel | null {
+  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
+    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
+      return null;
+    }
+
+    const dragTarget = this.getDragTarget({ x, y });
+
+    if (!dragTarget) {
+      return null;
+    }
+
+    if (dragTarget === 'poc') {
+      return {
+        cursorStyle: 'ew-resize',
+        externalId: 'volume-profile',
+        zOrder: 'top',
+      };
+    }
+
+    return {
+      cursorStyle: this.isActive.value ? 'grab' : 'pointer',
+      externalId: 'volume-profile',
+      zOrder: 'top',
+    };
+  }
+
+  protected getTimeAxisLabel(kind: string): AxisLabel | null {
     if (this.profileKind === 'visibleRange') {
       return null;
     }
@@ -444,7 +469,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     };
   }
 
-  public getPriceAxisLabel(kind: string): AxisLabel | null {
+  protected getPriceAxisLabel(kind: string): AxisLabel | null {
     if (this.profileKind === 'visibleRange') {
       return null;
     }
@@ -475,32 +500,6 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     };
   }
 
-  public hitTest(x: number, y: number): PrimitiveHoveredItem | null {
-    if (this.hidden || this.mode === 'idle' || this.mode === 'drawing') {
-      return null;
-    }
-
-    const dragTarget = this.getDragTarget({ x, y });
-
-    if (!dragTarget) {
-      return null;
-    }
-
-    if (dragTarget === 'poc') {
-      return {
-        cursorStyle: 'ew-resize',
-        externalId: 'volume-profile',
-        zOrder: 'top',
-      };
-    }
-
-    return {
-      cursorStyle: this.isActive.value ? 'grab' : 'pointer',
-      externalId: 'volume-profile',
-      zOrder: 'top',
-    };
-  }
-
   private handleVisibleLogicalRangeChange = (): void => {
     if (this.profileKind !== 'visibleRange') {
       return;
@@ -510,35 +509,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     this.render();
   };
 
-  protected bindEvents(): void {
-    if (this.isBound) {
-      return;
-    }
-
-    this.isBound = true;
-
-    this.container.addEventListener('dblclick', this.handleDoubleClick);
-    this.container.addEventListener('pointerdown', this.handlePointerDown);
-    window.addEventListener('pointermove', this.handlePointerMove);
-    window.addEventListener('pointerup', this.handlePointerUp);
-    window.addEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  protected unbindEvents(): void {
-    if (!this.isBound) {
-      return;
-    }
-
-    this.isBound = false;
-
-    this.container.removeEventListener('dblclick', this.handleDoubleClick);
-    this.container.removeEventListener('pointerdown', this.handlePointerDown);
-    window.removeEventListener('pointermove', this.handlePointerMove);
-    window.removeEventListener('pointerup', this.handlePointerUp);
-    window.removeEventListener('pointercancel', this.handlePointerUp);
-  }
-
-  private handleDoubleClick = (event: MouseEvent): void => {
+  protected handleDoubleClick = (event: MouseEvent): void => {
     if (this.hidden || this.mode !== 'ready' || !this.isActive.value) {
       return;
     }
@@ -555,7 +526,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     this.openSettings?.();
   };
 
-  private handlePointerDown = (event: PointerEvent): void => {
+  protected handlePointerDown = (event: PointerEvent): void => {
     if (this.hidden || event.button !== 0) {
       return;
     }
@@ -570,7 +541,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     this.handleFixedRangePointerDown(event, point);
   };
 
-  private handleVisibleRangePointerDown(event: PointerEvent, point: Point): void {
+  protected handleVisibleRangePointerDown(event: PointerEvent, point: Point): void {
     const dragTarget = this.getVisibleRangeDragTarget(point);
 
     if (!dragTarget) {
@@ -646,7 +617,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     this.startDragging(point, event.pointerId, dragTarget);
   }
 
-  private handlePointerMove = (event: PointerEvent): void => {
+  protected handlePointerMove = (event: PointerEvent): void => {
     const point = this.getEventPoint(event);
 
     if (this.profileKind === 'visibleRange') {
@@ -691,7 +662,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     this.render();
   }
 
-  private handlePointerUp = (event: PointerEvent): void => {
+  protected handlePointerUp = (event: PointerEvent): void => {
     if (this.mode !== 'dragging' || this.dragPointerId !== event.pointerId) {
       return;
     }
@@ -931,14 +902,14 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
 
   private calculateProfile(): void {
     if (this.profileKind === 'visibleRange') {
-      this.calculateVisibleRangeProfile();
+      this.calculateAnchoredVolumeProfile();
       return;
     }
 
-    this.calculateFixedRangeProfile();
+    this.calculateFixedRangeVolumeProfile();
   }
 
-  private calculateVisibleRangeProfile(): void {
+  private calculateAnchoredVolumeProfile(): void {
     const visibleRange = this.chart.timeScale().getVisibleLogicalRange();
 
     if (!visibleRange) {
@@ -953,31 +924,33 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     this.calculateProfileByLogicalRange(start, to);
   }
 
-  private calculateFixedRangeProfile(): void {
+  private calculateFixedRangeVolumeProfile(): void {
     if (!this.startAnchor || !this.endAnchor) {
       this.clearProfile();
       return;
     }
 
-    const leftTime = Math.min(Number(this.startAnchor.time), Number(this.endAnchor.time));
-    const rightTime = Math.max(Number(this.startAnchor.time), Number(this.endAnchor.time));
+    const leftFrameTime = Math.min(Number(this.startAnchor.time), Number(this.endAnchor.time));
+    const rightFrameTime = Math.max(Number(this.startAnchor.time), Number(this.endAnchor.time));
 
-    if (!Number.isFinite(leftTime) || !Number.isFinite(rightTime)) {
-      this.clearProfile();
-      return;
-    }
+    const candles = this.series.data() as SeriesCandleData[];
+    const selectedCandles: SeriesCandleData[] = [];
+
+    let maxFramePrice = Math.max(this.startAnchor.price, this.startAnchor.price);
+    let minFramePrice = Math.min(this.startAnchor.price, this.startAnchor.price);
 
-    const candles = (this.series.data() as SeriesCandleData[]).filter((candle) => {
-      const time = Number(candle.time);
+    candles.forEach((candle) => {
+      const candleTime = Number(candle.time);
+
+      if (candleTime >= leftFrameTime && candleTime <= rightFrameTime) {
+        maxFramePrice = Math.max(candle.high ?? candle.value ?? -Infinity, maxFramePrice);
+        minFramePrice = Math.min(candle.low ?? candle.value ?? Infinity, minFramePrice);
 
-      return Number.isFinite(time) && time >= leftTime && time <= rightTime;
+        selectedCandles.push(candle);
+      }
     });
 
-    this.calculateProfileByCandles(
-      candles,
-      Math.min(this.startAnchor.price, this.endAnchor.price),
-      Math.max(this.startAnchor.price, this.endAnchor.price),
-    );
+    this.calculateProfileByCandles(selectedCandles);
   }
 
   private calculateProfileByLogicalRange(fromLogical: number, toLogical: number): void {
@@ -1059,7 +1032,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
       const isBuyVolume = this.isBuyVolume(candle);
 
       if (candleHigh === candleLow) {
-        this.addPointVolume(profileRows, price, volume, isBuyVolume);
+        addPointVolume(profileRows, candleHigh, volume, isBuyVolume);
         return;
       }
 
@@ -1074,7 +1047,7 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
           return;
         }
 
-        this.addVolumeToRow(row, volume * (overlap / candleRange), isBuyVolume);
+        addVolumeToRow(row, volume * (overlap / candleRange), isBuyVolume);
       });
     });
 
@@ -1148,48 +1121,19 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     return close >= open;
   }
 
-  private addPointVolume(
-    profileRows: VolumeProfileDataRow[],
-    price: number,
-    volume: number,
-    isBuyVolume: boolean,
-  ): void {
-    const row = profileRows.find((item, index) => {
-      const isLastRow = index === profileRows.length - 1;
-
-      return price >= item.priceLow && (price < item.priceHigh || isLastRow);
-    });
-
-    if (!row) {
-      return;
-    }
-
-    this.addVolumeToRow(row, volume, isBuyVolume);
-  }
-
-  private addVolumeToRow(row: VolumeProfileDataRow, volume: number, isBuyVolume: boolean): void {
-    if (isBuyVolume) {
-      row.buyVolume += volume;
-    } else {
-      row.sellVolume += volume;
-    }
-
-    row.totalVolume += volume;
-  }
-
   private getNumber(value: unknown): number | null {
     return typeof value === 'number' && Number.isFinite(value) ? value : null;
   }
 
   protected getGeometry(): VolumeProfileGeometry | null {
     if (this.profileKind === 'visibleRange') {
-      return this.getVisibleRangeGeometry();
+      return this.getAnchoredVolumeProfileGeometry();
     }
 
-    return this.getFixedRangeGeometry();
+    return this.getFixedRangeVolumeProfileGeometry();
   }
 
-  private getVisibleRangeGeometry(): VolumeProfileGeometry | null {
+  private getAnchoredVolumeProfileGeometry(): VolumeProfileGeometry | null {
     if (this.profileMinPrice === null || this.profileMaxPrice === null) {
       return null;
     }
@@ -1226,15 +1170,19 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     };
   }
 
-  private getFixedRangeGeometry(): VolumeProfileGeometry | null {
+  private getFixedRangeVolumeProfileGeometry(): VolumeProfileGeometry | null {
     if (!this.startAnchor || !this.endAnchor) {
       return null;
     }
 
+    if (this.profileMinPrice === null || this.profileMaxPrice === null) {
+      return null;
+    }
+
     const startX = getXCoordinateFromTime(this.chart, this.startAnchor.time, this.series);
     const endX = getXCoordinateFromTime(this.chart, this.endAnchor.time, this.series);
-    const startY = getYCoordinateFromPrice(this.series, this.startAnchor.price);
-    const endY = getYCoordinateFromPrice(this.series, this.endAnchor.price);
+    const startY = getYCoordinateFromPrice(this.series, this.profileMinPrice);
+    const endY = getYCoordinateFromPrice(this.series, this.profileMaxPrice);
 
     if (startX === null || endX === null || startY === null || endY === null) {
       return null;
@@ -1331,3 +1279,32 @@ export class VolumeProfile extends SeriesDrawingBase<VolumeProfileSettings> impl
     return clampPointToContainerInElement(point, this.container);
   }
 }
+
+function addPointVolume(
+  profileRows: VolumeProfileDataRow[],
+  price: number,
+  volume: number,
+  isBuyVolume: boolean,
+): void {
+  const row = profileRows.find((item, index) => {
+    const isLastRow = index === profileRows.length - 1;
+
+    return price >= item.priceLow && (price < item.priceHigh || isLastRow);
+  });
+
+  if (!row) {
+    return;
+  }
+
+  addVolumeToRow(row, volume, isBuyVolume);
+}
+
+function addVolumeToRow(row: VolumeProfileDataRow, volume: number, isBuyVolume: boolean): void {
+  if (isBuyVolume) {
+    row.buyVolume += volume;
+  } else {
+    row.sellVolume += volume;
+  }
+
+  row.totalVolume += volume;
+}
diff --git a/src/core/DrawingsManager.tsx b/src/core/DrawingsManager.tsx
index a879e9903d3c4e64b1b1abf75124edf89612f2b3..e4f85b64aa0f26c156fc1d626fac5eebf046c857 100644
--- a/src/core/DrawingsManager.tsx
+++ b/src/core/DrawingsManager.tsx
@@ -250,12 +250,21 @@ export class DrawingsManager {
 
     let createdDrawing: Drawing | null = null;
 
-    const construct = (chart: IChartApi, series: ISeriesApi<SeriesType>) =>
-      config.construct({
+    const construct = (chart: IChartApi, series: ISeriesApi<SeriesType>) => {
+      const paneElement = series.getPane().getHTMLElement();
+
+      if (!paneElement) {
+        throw new Error('[Drawing Manager]: cannot place drawing, there is no pane');
+      }
+
+      const cells = paneElement.querySelectorAll<HTMLTableCellElement>(':scope > td');
+      const canvasElement = cells.item(1);
+
+      return config.construct({
         chart,
         series,
         eventManager: this.eventManager,
-        container: this.container,
+        container: canvasElement,
         removeSelf: () => this.removeDrawing(drawingId),
         openSettings: () => {
           if (createdDrawing) {
@@ -263,6 +272,7 @@ export class DrawingsManager {
           }
         },
       });
+    };
 
     const drawingFactory = (zIndex: number, moveUp: (id: string) => void, moveDown: (id: string) => void) =>
       new Drawing({
diff --git a/src/core/Series/BaseSeries.ts b/src/core/Series/BaseSeries.ts
index 335be26bfb487b10723c7d1a750d74d83b0f8bdb..ff74500b01d18fed3bcb206e9fe1595082e5de51 100644
--- a/src/core/Series/BaseSeries.ts
+++ b/src/core/Series/BaseSeries.ts
@@ -443,7 +443,7 @@ export function calcCandleChange(
     return {
       ...current,
       absoluteChange,
-      percentageChange,
+      percentageChange: Number.isNaN(percentageChange) ? 0 : percentageChange,
     };
   }
 
@@ -459,7 +459,7 @@ export function calcCandleChange(
       high: typeof high === 'number' ? high : current.value,
       low: typeof low === 'number' ? low : current.value,
       absoluteChange,
-      percentageChange,
+      percentageChange: Number.isNaN(percentageChange) ? 0 : percentageChange,
       customValues: current.customValues,
     };
   }
diff --git a/src/translations/russianDict.ts b/src/translations/russianDict.ts
index 97958be08505b6360363667f62e770e8438b329d..6d50a79f1e1bc4cd446434531b5a40804bd76128 100644
--- a/src/translations/russianDict.ts
+++ b/src/translations/russianDict.ts
@@ -46,8 +46,8 @@ export const russian = {
   'Short position': 'Короткая позиция',
   'Dates range': 'Диапазон дат',
   'Prices range': 'Диапазон цен',
-  'Fixed profile volume': 'Фиксированный профиль объема',
-  'Visible profile volume range': 'Профиль объема видимого диапазона',
+  'Fixed range profile volume': 'Профиль объема в заданном диапазоне',
+  'Anchored volume profile': 'Профиль объема с привязкой',
   'Fibonacci retracement': 'Коррекция Фибоначчи',
   Rectangle: 'Прямоугольник',
   Traectory: 'Траектория',
diff --git a/src/utils/formatValues.ts b/src/utils/formatValues.ts
index 2b23849eb912f61fa897e97daf0fe02748d6aebe..0b568b64798cbfbd55cd050624a54d1a1eeb4919 100644
--- a/src/utils/formatValues.ts
+++ b/src/utils/formatValues.ts
@@ -13,6 +13,16 @@ export function formatCompactNumber(
   locale: Locale = getLocale(),
   maxFractionDigits: number = getPricePrecision(),
 ): string {
+  const formatter = new Intl.NumberFormat(locale, {
+    maximumFractionDigits: maxFractionDigits,
+    minimumFractionDigits: 0,
+    useGrouping: false,
+  });
+
+  if (value === Infinity || value === -Infinity) {
+    return formatter.format(value);
+  }
+
   const unitList: {
     divisor: number;
     suffix: string;
@@ -38,12 +48,6 @@ export function formatCompactNumber(
   }
   const divided = value / selectedUnit.divisor;
 
-  const formatter = new Intl.NumberFormat(locale, {
-    maximumFractionDigits: maxFractionDigits,
-    minimumFractionDigits: 0,
-    useGrouping: false,
-  });
-
   return formatter.format(divided) + selectedUnit.suffix;
 }