Skip to content

Commit

Permalink
Merge pull request #180 from devexperts/prepare-candle-fix
Browse files Browse the repository at this point in the history
fix: correct usage of prepare candle error throw
  • Loading branch information
KirillBobkov authored May 17, 2024
2 parents a4e6601 + e6436a0 commit af0ed34
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 29 deletions.
49 changes: 28 additions & 21 deletions src/chart/components/chart/candle.functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,33 @@ import { PartialCandle } from './chart.component';
* so there is not enough information to build a candle: only Open/Close value available.
* In this case Daily candle, which we receive, must be completed to full OHLC with equal values.
*/
export const prepareCandle = (candle: PartialCandle): Candle => {
const settlementPrice = finite(candle.close, candle.open, candle.hi, candle.lo);
if (!isFinite(settlementPrice)) {
throw new Error('Received candle without any price');
export const prepareCandle = (candle: PartialCandle): Candle | undefined => {
try {
const settlementPrice = finite(candle.close, candle.open, candle.hi, candle.lo);
if (!isFinite(settlementPrice)) {
throw new Error('Received candle without any price');
}
// @ts-ignore
const preparedCandleHi = finite(candle.hi, Math.max(candle.open, candle.close), settlementPrice);
// @ts-ignore
const preparedCandleLo = finite(candle.lo, Math.min(candle.open, candle.close), settlementPrice);
const preparedCandleOpen = finite(candle.open, candle.lo, settlementPrice);
const preparedCandleClose = finite(candle.close, candle.hi, settlementPrice);
return {
hi: preparedCandleHi,
lo: preparedCandleLo,
open: preparedCandleOpen,
close: preparedCandleClose,
timestamp: candle.timestamp,
volume: candle.volume ?? 0,
expansion: candle.expansion,
idx: candle.idx,
impVolatility: candle.impVolatility,
};
} catch (e) {
console.warn(e);
return;
}
// @ts-ignore
const preparedCandleHi = finite(candle.hi, Math.max(candle.open, candle.close), settlementPrice);
// @ts-ignore
const preparedCandleLo = finite(candle.lo, Math.min(candle.open, candle.close), settlementPrice);
const preparedCandleOpen = finite(candle.open, candle.lo, settlementPrice);
const preparedCandleClose = finite(candle.close, candle.hi, settlementPrice);
return {
hi: preparedCandleHi,
lo: preparedCandleLo,
open: preparedCandleOpen,
close: preparedCandleClose,
timestamp: candle.timestamp,
volume: candle.volume ?? 0,
expansion: candle.expansion,
idx: candle.idx,
impVolatility: candle.impVolatility,
};
};

/**
Expand All @@ -51,3 +56,5 @@ export const deleteCandlesIndex = (candles: Array<Candle>) => {
candle.idx = undefined;
});
};

export const isCandle = (value: Candle | undefined): value is Candle => value !== undefined;
18 changes: 10 additions & 8 deletions src/chart/components/chart/chart.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { PaneComponent } from '../pane/pane.component';
import { LabelGroup } from '../y_axis/price_labels/y-axis-labels.model';
import { createBasicScaleViewportTransformer, createTimeFrameViewportTransformer } from './basic-scale';
import { calculateCandleWidth } from './candle-width-calculator.functions';
import { deleteCandlesIndex, prepareCandle, reindexCandles } from './candle.functions';
import { deleteCandlesIndex, isCandle, prepareCandle, reindexCandles } from './candle.functions';
import { ChartBaseModel } from './chart-base.model';
import { CandleSeries, ChartInstrument, PartialCandle } from './chart.component';
import { fakeCandle } from './fake-candles';
Expand Down Expand Up @@ -230,9 +230,9 @@ export class ChartModel extends ChartBaseElement {
instrument: ChartInstrument = this.mainCandleSeries.instrument,
recalculateAndUpdate = true,
): CandleSeriesModel | undefined {
const prepareCandleCandles = sortCandles(candles.map(prepareCandle));
const preparedCandles = prepareCandles(candles);
// set correct indexes based on main candles timestamp
const reindexCandles = this.reindexCandlesBasedOnSeries(this.mainCandleSeries.dataPoints, prepareCandleCandles);
const reindexCandles = this.reindexCandlesBasedOnSeries(this.mainCandleSeries.dataPoints, preparedCandles);
// ensure there are no gaps in new candles
const secondaryCandles = this.secondarySeriesAdjustments(this.mainCandleSeries.dataPoints, reindexCandles);
// create a new secondary series model if it doesn't already exist
Expand Down Expand Up @@ -267,10 +267,10 @@ export class ChartModel extends ChartBaseElement {
this.mainInstrumentChangedSubject.next(mainSeries.instrument);
}
this.rememberCurrentTimeframe();
const prepareCandleCandles = sortCandles(mainSeries.candles.map(prepareCandle));
const preparedCandles = prepareCandles(mainSeries.candles);
this.mainCandleSeries.clearData();
reindexCandles(prepareCandleCandles);
this.mainCandleSeries.dataPoints = prepareCandleCandles;
reindexCandles(preparedCandles);
this.mainCandleSeries.dataPoints = preparedCandles;
// deactivate deleted series
this.secondaryCandleSeries
.filter(series => {
Expand Down Expand Up @@ -359,15 +359,15 @@ export class ChartModel extends ChartBaseElement {
return;
}

const preparedCandles = sortCandles(mainSeries.candles.map(prepareCandle));
const preparedCandles = prepareCandles(mainSeries.candles);
const updateResult = updateCandles(this.mainCandleSeries.dataPoints, preparedCandles);
const updatedCandles = updateResult.candles;
reindexCandles(updatedCandles);
this.mainCandleSeries.dataPoints = updatedCandles;

// re-create series
secondarySeries.map(series => {
const preparedCandles = sortCandles(series.candles.map(prepareCandle));
const preparedCandles = prepareCandles(series.candles);
const updatedCandles = updateCandles(
this.findSecondarySeriesBySymbol(series.instrument?.symbol ?? '')?.dataPoints ?? [],
preparedCandles,
Expand Down Expand Up @@ -1117,6 +1117,8 @@ export interface UpdateCandlesResult {
const sortCandles = (candles: Candle[]): Candle[] =>
candles.slice().sort((a, b) => (a.timestamp === b.timestamp ? 0 : a.timestamp > b.timestamp ? 1 : -1));

const prepareCandles = (candles: PartialCandle[]): Candle[] => sortCandles(candles.map(prepareCandle).filter(isCandle));

const findFirstNotEmptyCandle = (candles: Array<Candle>, startIdx: number, iterateStep: number): Candle | undefined => {
if (startIdx >= candles.length) {
return candles[candles.length - 1];
Expand Down

0 comments on commit af0ed34

Please sign in to comment.