Skip to content

Commit

Permalink
Osama/DERG-1442/Add closed accumulator barriers (#291)
Browse files Browse the repository at this point in the history
- add Accumulators closed barrier indicator
  • Loading branch information
osama-deriv authored Jan 19, 2024
1 parent 25d1398 commit 0a3353f
Show file tree
Hide file tree
Showing 7 changed files with 384 additions and 9 deletions.
1 change: 1 addition & 0 deletions lib/deriv_chart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ library deriv_chart;
export 'generated/l10n.dart';
export 'src/deriv_chart/chart/chart.dart';
export 'src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_indicator.dart';
export 'src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_closed_indicator.dart';
export 'src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_entry_spot_barrier.dart';
export 'src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_active_contract.dart';
export 'src/deriv_chart/chart/data_visualization/annotations/barriers/barrier.dart';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import 'package:deriv_chart/deriv_chart.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/accumulator_object.dart';

import 'accumulators_closed_indicator_painter.dart';

/// Accumulator Closed Contract Barriers.
class AccumulatorsClosedIndicator extends ChartAnnotation<AccumulatorObject> {
/// Initializes a tick indicator.
AccumulatorsClosedIndicator(
this.exitTick, {
required this.lowBarrier,
required this.highBarrier,
required this.highBarrierDisplay,
required this.lowBarrierDisplay,
required this.barrierSpotDistance,
required this.barrierEpoch,
required this.activeContract,
this.barrierEndEpoch,
super.style,
String? id,
}) : super(id ?? 'AccumulatorsClosedIndicator');

/// The price difference between the barrier and the barrier Tick quote.
final String barrierSpotDistance;

/// The which this tick indicator will be pointing to.
final Tick exitTick;

/// The low barrier value.
final double lowBarrier;

/// The high barrier value.
final double highBarrier;

/// The low barrier display value.
final String highBarrierDisplay;

/// The high barrier display value.
final String lowBarrierDisplay;

/// [Optional] Active contract information.
final AccumulatorsActiveContract? activeContract;

/// The [epoch] of the tick that the barriers belong to.
final int barrierEpoch;

/// The End if the barrier, if [null] the barriers will go to the end of the screen.
final int? barrierEndEpoch;

@override
SeriesPainter<Series> createPainter() =>
AccumulatorsClosedIndicatorPainter(this);

@override
AccumulatorObject createObject() => AccumulatorObject(
tick: exitTick,
barrierEpoch: barrierEpoch,
lowBarrier: lowBarrier,
highBarrier: highBarrier,
profit: activeContract?.profit,
barrierEndEpoch: barrierEndEpoch,
);

@override
int? getMaxEpoch() => barrierEpoch;

@override
int? getMinEpoch() => barrierEpoch;

@override
List<double> recalculateMinMax() {
if (annotationObject.bottomValue == null ||
annotationObject.topValue == null ||
!isOnRange) {
return <double>[double.nan, double.nan];
}
final double halfOfBarriersDelta =
(annotationObject.highBarrier - annotationObject.lowBarrier) / 2;

return <double>[
annotationObject.bottomValue! - halfOfBarriersDelta,
annotationObject.topValue! + halfOfBarriersDelta,
];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
import 'package:deriv_chart/deriv_chart.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_data.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/animation_info.dart';
import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_dot.dart';
import 'package:deriv_chart/src/deriv_chart/chart/helpers/paint_functions/paint_text.dart';
import 'package:flutter/material.dart';

/// Accumulator barriers painter.
class AccumulatorsClosedIndicatorPainter
extends SeriesPainter<AccumulatorsClosedIndicator> {
/// Initializes [AccumulatorsClosedIndicatorPainter].
AccumulatorsClosedIndicatorPainter(super.series);

final Paint _linePaint = Paint()
..strokeWidth = 1
..style = PaintingStyle.stroke;

final Paint _linePaintFill = Paint()
..strokeWidth = 1
..style = PaintingStyle.fill;

final Paint _rectPaint = Paint()..style = PaintingStyle.fill;

/// Padding between lines.
static const double padding = 4;

/// Padding between tick and text.
static const double tickTextPadding = 12;

/// Right margin.
static const double rightMargin = 4;

@override
void onPaint({
required Canvas canvas,
required Size size,
required EpochToX epochToX,
required QuoteToY quoteToY,
required AnimationInfo animationInfo,
}) {
final HorizontalBarrierStyle style =
series.style as HorizontalBarrierStyle? ?? theme.horizontalBarrierStyle;

// Change the barrier color based on the contract status and tick quote.
Color color = theme.base03Color;
if (series.activeContract?.profit != null) {
if (series.activeContract!.profit! > 0) {
color = theme.accentGreenColor;
} else if (series.activeContract!.profit! < 0) {
color = theme.accentRedColor;
}
}

if (series.exitTick.quote > series.highBarrier ||
series.exitTick.quote < series.lowBarrier) {
color = theme.accentRedColor;
}
_linePaint.color = color;
_linePaintFill.color = color;
_rectPaint.color = color.withOpacity(0.08);

final AccumulatorsClosedIndicator indicator = series;

final double barrierX = epochToX(indicator.barrierEpoch);
final double hBarrierQuote = indicator.highBarrier;
final double lBarrierQuote = indicator.lowBarrier;

final Offset highBarrierPosition = Offset(
barrierX,
quoteToY(hBarrierQuote),
);

final Offset lowBarrierPosition = Offset(
barrierX,
quoteToY(lBarrierQuote),
);

final Offset exitTickPosition = Offset(
epochToX(indicator.exitTick.epoch),
quoteToY(indicator.exitTick.quote),
);

// draw the transparent color.
final Rect rect = Rect.fromPoints(
highBarrierPosition,
Offset(
series.barrierEndEpoch != null
? epochToX(series.barrierEndEpoch!)
: size.width,
lowBarrierPosition.dy),
);
canvas.drawRect(rect, _rectPaint);

const int triangleEdge = 4;
const int triangleHeight = 5;

final Path upperTrianglePath = Path()
..moveTo(
highBarrierPosition.dx,
highBarrierPosition.dy,
)
..lineTo(
highBarrierPosition.dx + triangleEdge,
highBarrierPosition.dy,
)
..lineTo(
highBarrierPosition.dx,
highBarrierPosition.dy + triangleHeight,
)
..lineTo(
highBarrierPosition.dx + -triangleEdge,
highBarrierPosition.dy,
)
..close();

final Path lowerTrianglePath = Path()
..moveTo(
lowBarrierPosition.dx,
lowBarrierPosition.dy,
)
..lineTo(
lowBarrierPosition.dx + triangleEdge,
lowBarrierPosition.dy,
)
..lineTo(
lowBarrierPosition.dx,
lowBarrierPosition.dy - triangleHeight,
)
..lineTo(
lowBarrierPosition.dx + -triangleEdge,
lowBarrierPosition.dy,
)
..close();

canvas
..drawLine(
lowBarrierPosition,
Offset(
series.barrierEndEpoch != null
? epochToX(series.barrierEndEpoch!)
: size.width,
lowBarrierPosition.dy),
_linePaint,
)
..drawLine(
highBarrierPosition,
Offset(
series.barrierEndEpoch != null
? epochToX(series.barrierEndEpoch!)
: size.width,
highBarrierPosition.dy),
_linePaint,
)
..drawPath(upperTrianglePath, _linePaint)
..drawPath(lowerTrianglePath, _linePaint)
..drawPath(upperTrianglePath, _linePaintFill)
..drawPath(lowerTrianglePath, _linePaintFill);

// Draw exit tick position.
paintDot(canvas, exitTickPosition, color);

// draw dialog
const double dotPadding = 5;
const int dialogTriangleEdge = 6;
const int dialogTriangleHeight = 4;

final Path dialogTrianglePath = Path()
..moveTo(
exitTickPosition.dx - dotPadding,
exitTickPosition.dy,
)
..lineTo(
exitTickPosition.dx - dotPadding - dialogTriangleHeight,
exitTickPosition.dy - dialogTriangleEdge / 2,
)
..lineTo(
exitTickPosition.dx - dotPadding - dialogTriangleHeight,
exitTickPosition.dy + dialogTriangleEdge / 2,
)
..lineTo(
exitTickPosition.dx - dotPadding,
exitTickPosition.dy,
)
..close();

canvas
..drawPath(dialogTrianglePath, _linePaintFill)
..drawPath(dialogTrianglePath, _linePaint);

if (indicator.activeContract?.profit != null) {
final double profit = indicator.activeContract!.profit!;
final String profitText =
'${profit < 0 ? '' : '+'}${profit.toStringAsFixed(2)}';
final String currencyText = '${indicator.activeContract?.currency ?? ''}';
final TextPainter profitPainter = makeTextPainter(
'$profitText $currencyText',
style.textStyle.copyWith(
color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500),
);

final TextPainter winLossPainter = makeTextPainter(
indicator.activeContract!.profit! > 0 ? 'Won:' : 'Loss:',
style.textStyle.copyWith(
color: Colors.white, fontSize: 10, fontWeight: FontWeight.w400),
);

final double textHeight = profitPainter.height + winLossPainter.height;
final double textWidth = profitPainter.width;

final double dialogRightSide =
exitTickPosition.dx - dotPadding - dialogTriangleHeight - padding;
final double dialogLeftSide = dialogRightSide - textWidth;

final double dialogDownSide = exitTickPosition.dy + textHeight / 2;
final double dialogUpSide = exitTickPosition.dy - textHeight / 2;

final Rect dialogRect = Rect.fromLTRB(
dialogLeftSide - padding,
dialogUpSide - padding,
dialogRightSide + padding,
dialogDownSide + padding,
);
final RRect rRect =
RRect.fromRectAndRadius(dialogRect, const Radius.circular(4));

_rectPaint.color = color.withOpacity(1);
canvas.drawRRect(rRect, _rectPaint);

final Offset winLossPosition = Offset(
dialogLeftSide + winLossPainter.width / 2,
exitTickPosition.dy - textHeight / 2 + winLossPainter.height / 2,
);

paintWithTextPainter(
canvas,
painter: winLossPainter,
anchor: winLossPosition,
);

final Offset profitPosition = Offset(
dialogLeftSide + profitPainter.width / 2,
exitTickPosition.dy -
textHeight / 2 +
profitPainter.height / 2 +
winLossPainter.height,
);

paintWithTextPainter(
canvas,
painter: profitPainter,
anchor: profitPosition,
);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:deriv_chart/deriv_chart.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/annotations/barriers/accumulators_barriers/accumulators_active_contract.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/chart_series/series_painter.dart';
import 'package:deriv_chart/src/deriv_chart/chart/data_visualization/models/accumulator_object.dart';

Expand Down Expand Up @@ -70,4 +69,20 @@ class AccumulatorIndicator extends ChartAnnotation<AccumulatorObject> {

@override
int? getMinEpoch() => barrierEpoch;

@override
List<double> recalculateMinMax() {
if (annotationObject.bottomValue == null ||
annotationObject.topValue == null ||
!isOnRange) {
return <double>[double.nan, double.nan];
}
final double halfOfBarriersDelta =
(annotationObject.highBarrier - annotationObject.lowBarrier) / 2;

return <double>[
annotationObject.bottomValue! - halfOfBarriersDelta,
annotationObject.topValue! + halfOfBarriersDelta,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,12 @@ abstract class ChartAnnotation<T extends ChartObject> extends Series {
isOnRange = annotationObject.isOnEpochRange(leftEpoch, rightEpoch);

@override
List<double> recalculateMinMax() => <double>[
annotationObject.bottomValue ?? double.nan,
annotationObject.topValue ?? double.nan
];
List<double> recalculateMinMax() => isOnRange
? <double>[
annotationObject.bottomValue ?? double.nan,
annotationObject.topValue ?? double.nan
]
: <double>[double.nan, double.nan];

/// Prepares the [annotationObject] of this [ChartAnnotation].
T createObject();
Expand Down
Loading

0 comments on commit 0a3353f

Please sign in to comment.