@tradingaction/series 2.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +5 -0
  3. package/lib/AlternateDataSeries.d.ts +5 -0
  4. package/lib/AlternateDataSeries.js +16 -0
  5. package/lib/AlternateDataSeries.js.map +1 -0
  6. package/lib/AlternatingFillAreaSeries.d.ts +77 -0
  7. package/lib/AlternatingFillAreaSeries.js +69 -0
  8. package/lib/AlternatingFillAreaSeries.js.map +1 -0
  9. package/lib/AreaOnlySeries.d.ts +42 -0
  10. package/lib/AreaOnlySeries.js +53 -0
  11. package/lib/AreaOnlySeries.js.map +1 -0
  12. package/lib/AreaSeries.d.ts +46 -0
  13. package/lib/AreaSeries.js +21 -0
  14. package/lib/AreaSeries.js.map +1 -0
  15. package/lib/BarSeries.d.ts +36 -0
  16. package/lib/BarSeries.js +91 -0
  17. package/lib/BarSeries.js.map +1 -0
  18. package/lib/BollingerSeries.d.ts +33 -0
  19. package/lib/BollingerSeries.js +59 -0
  20. package/lib/BollingerSeries.js.map +1 -0
  21. package/lib/CandlestickSeries.d.ts +65 -0
  22. package/lib/CandlestickSeries.js +115 -0
  23. package/lib/CandlestickSeries.js.map +1 -0
  24. package/lib/ElderRaySeries.d.ts +45 -0
  25. package/lib/ElderRaySeries.js +66 -0
  26. package/lib/ElderRaySeries.js.map +1 -0
  27. package/lib/GroupedBarSeries.d.ts +33 -0
  28. package/lib/GroupedBarSeries.js +22 -0
  29. package/lib/GroupedBarSeries.js.map +1 -0
  30. package/lib/KagiSeries.d.ts +45 -0
  31. package/lib/KagiSeries.js +108 -0
  32. package/lib/KagiSeries.js.map +1 -0
  33. package/lib/LineSeries.d.ts +84 -0
  34. package/lib/LineSeries.js +102 -0
  35. package/lib/LineSeries.js.map +1 -0
  36. package/lib/MACDSeries.d.ts +55 -0
  37. package/lib/MACDSeries.js +62 -0
  38. package/lib/MACDSeries.js.map +1 -0
  39. package/lib/OHLCSeries.d.ts +32 -0
  40. package/lib/OHLCSeries.js +67 -0
  41. package/lib/OHLCSeries.js.map +1 -0
  42. package/lib/OverlayBarSeries.d.ts +35 -0
  43. package/lib/OverlayBarSeries.js +71 -0
  44. package/lib/OverlayBarSeries.js.map +1 -0
  45. package/lib/PointAndFigureSeries.d.ts +33 -0
  46. package/lib/PointAndFigureSeries.js +92 -0
  47. package/lib/PointAndFigureSeries.js.map +1 -0
  48. package/lib/RSISeries.d.ts +68 -0
  49. package/lib/RSISeries.js +82 -0
  50. package/lib/RSISeries.js.map +1 -0
  51. package/lib/RenkoSeries.d.ts +42 -0
  52. package/lib/RenkoSeries.js +65 -0
  53. package/lib/RenkoSeries.js.map +1 -0
  54. package/lib/SARSeries.d.ts +34 -0
  55. package/lib/SARSeries.js +69 -0
  56. package/lib/SARSeries.js.map +1 -0
  57. package/lib/SVGComponent.d.ts +8 -0
  58. package/lib/SVGComponent.js +9 -0
  59. package/lib/SVGComponent.js.map +1 -0
  60. package/lib/ScatterSeries.d.ts +24 -0
  61. package/lib/ScatterSeries.js +60 -0
  62. package/lib/ScatterSeries.js.map +1 -0
  63. package/lib/StackedBarSeries.d.ts +44 -0
  64. package/lib/StackedBarSeries.js +157 -0
  65. package/lib/StackedBarSeries.js.map +1 -0
  66. package/lib/StochasticSeries.d.ts +39 -0
  67. package/lib/StochasticSeries.js +42 -0
  68. package/lib/StochasticSeries.js.map +1 -0
  69. package/lib/StraightLine.d.ts +38 -0
  70. package/lib/StraightLine.js +81 -0
  71. package/lib/StraightLine.js.map +1 -0
  72. package/lib/VolumeProfileSeries.d.ts +45 -0
  73. package/lib/VolumeProfileSeries.js +161 -0
  74. package/lib/VolumeProfileSeries.js.map +1 -0
  75. package/lib/index.d.ts +23 -0
  76. package/lib/index.js +24 -0
  77. package/lib/index.js.map +1 -0
  78. package/lib/markers/CircleMarker.d.ts +25 -0
  79. package/lib/markers/CircleMarker.js +35 -0
  80. package/lib/markers/CircleMarker.js.map +1 -0
  81. package/lib/markers/SquareMarker.d.ts +25 -0
  82. package/lib/markers/SquareMarker.js +37 -0
  83. package/lib/markers/SquareMarker.js.map +1 -0
  84. package/lib/markers/TriangleMarker.d.ts +27 -0
  85. package/lib/markers/TriangleMarker.js +89 -0
  86. package/lib/markers/TriangleMarker.js.map +1 -0
  87. package/lib/markers/index.d.ts +3 -0
  88. package/lib/markers/index.js +4 -0
  89. package/lib/markers/index.js.map +1 -0
  90. package/package.json +52 -0
  91. package/src/AlternateDataSeries.tsx +29 -0
  92. package/src/AlternatingFillAreaSeries.tsx +159 -0
  93. package/src/AreaOnlySeries.tsx +106 -0
  94. package/src/AreaSeries.tsx +95 -0
  95. package/src/BarSeries.tsx +151 -0
  96. package/src/BollingerSeries.tsx +90 -0
  97. package/src/CandlestickSeries.tsx +188 -0
  98. package/src/ElderRaySeries.tsx +99 -0
  99. package/src/GroupedBarSeries.tsx +48 -0
  100. package/src/KagiSeries.tsx +155 -0
  101. package/src/LineSeries.tsx +223 -0
  102. package/src/MACDSeries.tsx +110 -0
  103. package/src/OHLCSeries.tsx +116 -0
  104. package/src/OverlayBarSeries.tsx +115 -0
  105. package/src/PointAndFigureSeries.tsx +140 -0
  106. package/src/RSISeries.tsx +158 -0
  107. package/src/RenkoSeries.tsx +118 -0
  108. package/src/SARSeries.tsx +111 -0
  109. package/src/SVGComponent.tsx +13 -0
  110. package/src/ScatterSeries.tsx +105 -0
  111. package/src/StackedBarSeries.tsx +272 -0
  112. package/src/StochasticSeries.tsx +70 -0
  113. package/src/StraightLine.tsx +166 -0
  114. package/src/VolumeProfileSeries.tsx +247 -0
  115. package/src/index.ts +23 -0
  116. package/src/markers/CircleMarker.tsx +69 -0
  117. package/src/markers/SquareMarker.tsx +71 -0
  118. package/src/markers/TriangleMarker.tsx +131 -0
  119. package/src/markers/index.ts +3 -0
@@ -0,0 +1,115 @@
1
+ import { merge } from "d3-array";
2
+ import { ScaleContinuousNumeric, ScaleTime } from "d3-scale";
3
+ import * as React from "react";
4
+ import { first, functor, getAxisCanvas, GenericChartComponent, plotDataLengthBarWidth } from "@tradingaction/core";
5
+ import { drawOnCanvas2 } from "./StackedBarSeries";
6
+
7
+ export interface OverlayBarSeriesProps {
8
+ readonly baseAt?:
9
+ | number
10
+ | ((
11
+ xScale: ScaleContinuousNumeric<number, number> | ScaleTime<number, number>,
12
+ yScale: ScaleContinuousNumeric<number, number>,
13
+ datum: any,
14
+ ) => number);
15
+ readonly direction?: "up" | "down";
16
+ readonly stroke?: boolean;
17
+ readonly width?: any;
18
+ readonly widthRatio?: number;
19
+ readonly fillStyle?: string | ((data: any, y: number) => string);
20
+ readonly yAccessor: ((datum: any) => number | undefined)[];
21
+ readonly xScale?: ScaleContinuousNumeric<number, number>;
22
+ readonly yScale?: ScaleContinuousNumeric<number, number>;
23
+ readonly plotData?: number[];
24
+ readonly clip?: boolean;
25
+ }
26
+
27
+ export class OverlayBarSeries extends React.Component<OverlayBarSeriesProps> {
28
+ public static defaultProps = {
29
+ baseAt: (xScale: ScaleContinuousNumeric<number, number>, yScale: ScaleContinuousNumeric<number, number>) =>
30
+ first(yScale.range()),
31
+ clip: true,
32
+ direction: "up",
33
+ stroke: false,
34
+ fillStyle: "#4682B4",
35
+ widthRatio: 0.5,
36
+ width: plotDataLengthBarWidth,
37
+ };
38
+
39
+ public render() {
40
+ const { clip } = this.props;
41
+
42
+ return (
43
+ <GenericChartComponent
44
+ canvasToDraw={getAxisCanvas}
45
+ canvasDraw={this.drawOnCanvas}
46
+ clip={clip}
47
+ drawOn={["pan"]}
48
+ />
49
+ );
50
+ }
51
+
52
+ private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
53
+ const bars = this.getBars(moreProps);
54
+
55
+ drawOnCanvas2(this.props, ctx, bars);
56
+ };
57
+
58
+ private readonly getBars = (moreProps: any) => {
59
+ const {
60
+ xScale,
61
+ xAccessor,
62
+ chartConfig: { yScale },
63
+ plotData,
64
+ } = moreProps;
65
+
66
+ const { baseAt, fillStyle, stroke, yAccessor } = this.props;
67
+
68
+ const getFill = functor(fillStyle);
69
+ const getBase = functor(baseAt);
70
+ const widthFunctor = functor(this.props.width);
71
+
72
+ const width = widthFunctor(this.props, moreProps);
73
+ const offset = Math.floor(0.5 * width);
74
+
75
+ const bars = plotData.map((d: any) => {
76
+ const innerBars = yAccessor
77
+ .map((eachYAccessor: any, i: number) => {
78
+ const yValue = eachYAccessor(d);
79
+ if (yValue === undefined) {
80
+ return undefined;
81
+ }
82
+
83
+ const xValue = xAccessor(d);
84
+ const x = Math.round(xScale(xValue)) - offset;
85
+ const y = yScale(yValue);
86
+
87
+ return {
88
+ height: 0,
89
+ width: offset * 2,
90
+ x,
91
+ y,
92
+ stroke: stroke ? getFill(d, i) : "none",
93
+ fillStyle: getFill(d, i),
94
+ i,
95
+ };
96
+ })
97
+ .filter((yValue: any) => yValue !== undefined);
98
+
99
+ let b = getBase(xScale, yScale, d);
100
+ let h;
101
+ for (let i = innerBars.length - 1; i >= 0; i--) {
102
+ h = b - innerBars[i]!.y;
103
+ if (h < 0) {
104
+ innerBars[i]!.y = b;
105
+ h = -1 * h;
106
+ }
107
+ innerBars[i]!.height = h;
108
+ b = innerBars[i]!.y;
109
+ }
110
+ return innerBars;
111
+ });
112
+
113
+ return merge(bars);
114
+ };
115
+ }
@@ -0,0 +1,140 @@
1
+ import { ScaleContinuousNumeric } from "d3-scale";
2
+ import * as React from "react";
3
+ import { isDefined, isNotDefined, getAxisCanvas, GenericChartComponent } from "@tradingaction/core";
4
+
5
+ export interface PointAndFigureSeriesProps {
6
+ readonly className?: string;
7
+ readonly clip?: boolean;
8
+ readonly fill?: {
9
+ up: string;
10
+ down: string;
11
+ };
12
+ readonly stroke?: {
13
+ up: string;
14
+ down: string;
15
+ };
16
+ readonly strokeWidth?: number;
17
+ }
18
+
19
+ export class PointAndFigureSeries extends React.Component<PointAndFigureSeriesProps> {
20
+ public static defaultProps = {
21
+ className: "react-financial-charts-point-and-figure",
22
+ strokeWidth: 1,
23
+ stroke: {
24
+ up: "#6BA583",
25
+ down: "#FF0000",
26
+ },
27
+ fill: {
28
+ up: "none",
29
+ down: "none",
30
+ },
31
+ clip: true,
32
+ };
33
+
34
+ public render() {
35
+ const { clip } = this.props;
36
+
37
+ return (
38
+ <GenericChartComponent
39
+ clip={clip}
40
+ canvasDraw={this.drawOnCanvas}
41
+ canvasToDraw={getAxisCanvas}
42
+ drawOn={["pan"]}
43
+ />
44
+ );
45
+ }
46
+
47
+ private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
48
+ const {
49
+ xAccessor,
50
+ xScale,
51
+ chartConfig: { yScale },
52
+ plotData,
53
+ } = moreProps;
54
+
55
+ const columns = this.getColumns(xScale, xAccessor, yScale, plotData);
56
+
57
+ this.drawOnCanvasPrivate(ctx, this.props, columns);
58
+ };
59
+
60
+ private readonly getColumns = (
61
+ xScale: ScaleContinuousNumeric<number, number>,
62
+ xAccessor: any,
63
+ yScale: ScaleContinuousNumeric<number, number>,
64
+ plotData: any[],
65
+ ) => {
66
+ const width = xScale(xAccessor(plotData[plotData.length - 1])) - xScale(xAccessor(plotData[0]));
67
+
68
+ const columnWidth = width / (plotData.length - 1);
69
+
70
+ let anyBox;
71
+ let j = 0;
72
+ while (isNotDefined(anyBox)) {
73
+ if (isDefined(plotData[j].close)) {
74
+ anyBox = plotData[j].boxes[0];
75
+ } else {
76
+ break;
77
+ }
78
+ j++;
79
+ }
80
+
81
+ const boxHeight = Math.abs(yScale(anyBox.open) - yScale(anyBox.close));
82
+
83
+ const columns = plotData
84
+ .filter((d) => isDefined(d.close))
85
+ .map((d) => {
86
+ const boxes = d.boxes.map((box: any) => ({
87
+ columnWidth,
88
+ boxHeight,
89
+ open: yScale(box.open),
90
+ close: yScale(box.close),
91
+ }));
92
+
93
+ const xOffset = xScale(xAccessor(d)) - columnWidth / 2;
94
+ return {
95
+ boxes,
96
+ direction: d.direction,
97
+ offset: [xOffset, 0],
98
+ };
99
+ });
100
+ return columns;
101
+ };
102
+
103
+ private readonly drawOnCanvasPrivate = (ctx: CanvasRenderingContext2D, props: any, columns: any[]) => {
104
+ const { stroke, fill, strokeWidth } = props;
105
+
106
+ ctx.lineWidth = strokeWidth;
107
+
108
+ columns.forEach((col) => {
109
+ const [offsetX, offsetY] = col.offset;
110
+ col.boxes.forEach((box: any) => {
111
+ if (col.direction > 0) {
112
+ ctx.fillStyle = fill.up;
113
+ ctx.strokeStyle = stroke.up;
114
+
115
+ ctx.beginPath();
116
+
117
+ ctx.moveTo(offsetX, offsetY + box.open);
118
+ ctx.lineTo(offsetX + box.columnWidth, offsetY + box.close);
119
+ ctx.moveTo(offsetX, offsetY + box.close);
120
+ ctx.lineTo(offsetX + box.columnWidth, offsetY + box.open);
121
+
122
+ ctx.stroke();
123
+ } else {
124
+ ctx.fillStyle = fill.down;
125
+ ctx.strokeStyle = stroke.down;
126
+
127
+ ctx.beginPath();
128
+
129
+ const [x, y] = [offsetX + box.columnWidth / 2, offsetY + box.open + box.boxHeight / 2];
130
+ const [rx, ry] = [box.columnWidth / 2, box.boxHeight / 2];
131
+
132
+ ctx.ellipse(x, y, rx, ry, 0, 0, 2 * Math.PI);
133
+ ctx.stroke();
134
+ }
135
+ });
136
+ });
137
+
138
+ ctx.stroke();
139
+ };
140
+ }
@@ -0,0 +1,158 @@
1
+ import * as React from "react";
2
+ import { strokeDashTypes } from "@tradingaction/core";
3
+ import { LineSeries } from "./LineSeries";
4
+ import { StraightLine } from "./StraightLine";
5
+ import { SVGComponent } from "./SVGComponent";
6
+
7
+ export interface RSISeriesProps {
8
+ readonly className?: string;
9
+ readonly yAccessor: (data: any) => any;
10
+ readonly strokeStyle?: {
11
+ line: string;
12
+ top: string;
13
+ middle: string;
14
+ bottom: string;
15
+ outsideThreshold: string;
16
+ insideThreshold: string;
17
+ };
18
+ readonly strokeDasharray?: {
19
+ line: strokeDashTypes;
20
+ top: strokeDashTypes;
21
+ middle: strokeDashTypes;
22
+ bottom: strokeDashTypes;
23
+ };
24
+ readonly strokeWidth?: {
25
+ outsideThreshold: number;
26
+ insideThreshold: number;
27
+ top: number;
28
+ middle: number;
29
+ bottom: number;
30
+ };
31
+ readonly overSold?: number;
32
+ readonly middle?: number;
33
+ readonly overBought?: number;
34
+ }
35
+
36
+ /**
37
+ * The Relative Strength Index (RSI) is a momentum oscillator that measures the speed and change of price movements.
38
+ */
39
+ export class RSISeries extends React.Component<RSISeriesProps> {
40
+ public static defaultProps = {
41
+ className: "react-financial-charts-rsi-series",
42
+ strokeStyle: {
43
+ line: "#000000",
44
+ top: "#B8C2CC",
45
+ middle: "#8795A1",
46
+ bottom: "#B8C2CC",
47
+ outsideThreshold: "#b300b3",
48
+ insideThreshold: "#ffccff",
49
+ },
50
+ strokeDasharray: {
51
+ line: "Solid" as strokeDashTypes,
52
+ top: "ShortDash" as strokeDashTypes,
53
+ middle: "ShortDash" as strokeDashTypes,
54
+ bottom: "ShortDash" as strokeDashTypes,
55
+ },
56
+ strokeWidth: {
57
+ outsideThreshold: 1,
58
+ insideThreshold: 1,
59
+ top: 1,
60
+ middle: 1,
61
+ bottom: 1,
62
+ },
63
+ overSold: 70,
64
+ middle: 50,
65
+ overBought: 30,
66
+ };
67
+
68
+ private clipPathId1 = `rsi-clip-${String(Math.round(Math.random() * 10000 * 10000))}`;
69
+ private clipPathId2 = `rsi-clip-${String(Math.round(Math.random() * 10000 * 10000))}`;
70
+
71
+ public render() {
72
+ const {
73
+ className,
74
+ strokeStyle = RSISeries.defaultProps.strokeStyle,
75
+ strokeDasharray = RSISeries.defaultProps.strokeDasharray,
76
+ strokeWidth = RSISeries.defaultProps.strokeWidth,
77
+ } = this.props;
78
+ const { yAccessor } = this.props;
79
+ const { overSold, middle, overBought } = this.props;
80
+
81
+ return (
82
+ <g className={className}>
83
+ <SVGComponent>{this.renderClip}</SVGComponent>
84
+ <StraightLine
85
+ strokeStyle={strokeStyle.top}
86
+ yValue={overSold}
87
+ lineDash={strokeDasharray.top}
88
+ lineWidth={strokeWidth.top}
89
+ />
90
+ <StraightLine
91
+ strokeStyle={strokeStyle.middle}
92
+ yValue={middle}
93
+ lineDash={strokeDasharray.middle}
94
+ lineWidth={strokeWidth.middle}
95
+ />
96
+ <StraightLine
97
+ strokeStyle={strokeStyle.bottom}
98
+ yValue={overBought}
99
+ lineDash={strokeDasharray.bottom}
100
+ lineWidth={strokeWidth.bottom}
101
+ />
102
+ <LineSeries
103
+ canvasClip={this.topAndBottomClip}
104
+ yAccessor={yAccessor}
105
+ strokeStyle={strokeStyle.insideThreshold || strokeStyle.line}
106
+ strokeWidth={strokeWidth.insideThreshold}
107
+ strokeDasharray={strokeDasharray.line}
108
+ />
109
+ <LineSeries
110
+ canvasClip={this.mainClip}
111
+ yAccessor={yAccessor}
112
+ strokeStyle={strokeStyle.outsideThreshold || strokeStyle.line}
113
+ strokeWidth={strokeWidth.outsideThreshold}
114
+ strokeDasharray={strokeDasharray.line}
115
+ />
116
+ </g>
117
+ );
118
+ }
119
+
120
+ private readonly renderClip = (moreProps: any) => {
121
+ const { chartConfig } = moreProps;
122
+ const { overSold, overBought } = this.props;
123
+ const { yScale, width, height } = chartConfig;
124
+
125
+ return (
126
+ <defs>
127
+ <clipPath id={this.clipPathId1}>
128
+ <rect x={0} y={yScale(overSold)} width={width} height={yScale(overBought) - yScale(overSold)} />
129
+ </clipPath>
130
+ <clipPath id={this.clipPathId2}>
131
+ <rect x={0} y={0} width={width} height={yScale(overSold)} />
132
+ <rect x={0} y={yScale(overBought)} width={width} height={height - yScale(overBought)} />
133
+ </clipPath>
134
+ </defs>
135
+ );
136
+ };
137
+
138
+ private readonly mainClip = (ctx: CanvasRenderingContext2D, moreProps: any) => {
139
+ const { chartConfig } = moreProps;
140
+ const { overSold, overBought } = this.props;
141
+ const { yScale, width, height } = chartConfig;
142
+
143
+ ctx.beginPath();
144
+ ctx.rect(0, 0, width, yScale(overSold));
145
+ ctx.rect(0, yScale(overBought), width, height - yScale(overBought));
146
+ ctx.clip();
147
+ };
148
+
149
+ private readonly topAndBottomClip = (ctx: CanvasRenderingContext2D, moreProps: any) => {
150
+ const { chartConfig } = moreProps;
151
+ const { overSold, overBought } = this.props;
152
+ const { yScale, width } = chartConfig;
153
+
154
+ ctx.beginPath();
155
+ ctx.rect(0, yScale(overSold), width, yScale(overBought) - yScale(overSold));
156
+ ctx.clip();
157
+ };
158
+ }
@@ -0,0 +1,118 @@
1
+ import { ScaleContinuousNumeric } from "d3-scale";
2
+ import * as React from "react";
3
+ import { isDefined, getAxisCanvas, GenericChartComponent } from "@tradingaction/core";
4
+
5
+ export interface RenkoSeriesProps {
6
+ readonly clip?: boolean;
7
+ readonly fill?: {
8
+ up: string;
9
+ down: string;
10
+ partial: string;
11
+ };
12
+ readonly stroke?: {
13
+ up: string;
14
+ down: string;
15
+ };
16
+ readonly yAccessor?: (data: any) => any;
17
+
18
+ readonly onClickWhenHover?: (e: React.MouseEvent, moreProps: any) => void;
19
+ readonly onDoubleClickWhenHover?: (e: React.MouseEvent, moreProps: any) => void;
20
+ readonly onDoubleClick?: (e: React.MouseEvent, moreProps: any) => void;
21
+ readonly onContextMenu?: (e: React.MouseEvent, moreProps: any) => void;
22
+ readonly onClickOutside?: (e: React.MouseEvent, moreProps: any) => void;
23
+ }
24
+
25
+ export class RenkoSeries extends React.Component<RenkoSeriesProps> {
26
+ public static defaultProps = {
27
+ clip: true,
28
+ fill: {
29
+ up: "#26a69a",
30
+ down: "#ef5350",
31
+ partial: "#4682B4",
32
+ },
33
+ stroke: {
34
+ up: "none",
35
+ down: "none",
36
+ },
37
+ yAccessor: (d: any) => ({ open: d.open, high: d.high, low: d.low, close: d.close }),
38
+ };
39
+
40
+ public render() {
41
+ const { clip } = this.props;
42
+
43
+ return (
44
+ <GenericChartComponent
45
+ clip={clip}
46
+ canvasDraw={this.drawOnCanvas}
47
+ canvasToDraw={getAxisCanvas}
48
+ drawOn={["pan", "mousemove"]}
49
+ onClickWhenHover={this.props.onClickWhenHover}
50
+ onDoubleClickWhenHover={this.props.onDoubleClickWhenHover}
51
+ onDoubleClick={this.props.onDoubleClick}
52
+ onContextMenu={this.props.onContextMenu}
53
+ onClickOutside={this.props.onClickOutside}
54
+ />
55
+ );
56
+ }
57
+
58
+ private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
59
+ const {
60
+ xAccessor,
61
+ xScale,
62
+ chartConfig: { yScale },
63
+ plotData,
64
+ } = moreProps;
65
+
66
+ const renko = this.getRenko(plotData, xScale, xAccessor, yScale);
67
+
68
+ renko.forEach((d) => {
69
+ const { fillStyle, strokeStyle } = d;
70
+ ctx.beginPath();
71
+
72
+ if (strokeStyle !== undefined) {
73
+ ctx.strokeStyle = strokeStyle;
74
+ }
75
+ if (fillStyle !== undefined) {
76
+ ctx.fillStyle = fillStyle;
77
+ }
78
+
79
+ ctx.rect(d.x, d.y, d.width, d.height);
80
+ ctx.closePath();
81
+ ctx.fill();
82
+ });
83
+ };
84
+
85
+ private readonly getRenko = (
86
+ plotData: any[],
87
+ xScale: ScaleContinuousNumeric<number, number>,
88
+ xAccessor: any,
89
+ yScale: ScaleContinuousNumeric<number, number>,
90
+ ) => {
91
+ const { fill, stroke, yAccessor = RenkoSeries.defaultProps.yAccessor } = this.props;
92
+
93
+ const width = xScale(xAccessor(plotData[plotData.length - 1])) - xScale(xAccessor(plotData[0]));
94
+
95
+ const candleWidth = width / (plotData.length - 1);
96
+
97
+ return plotData
98
+ .filter((d) => isDefined(yAccessor(d).close))
99
+ .map((d) => {
100
+ const ohlc = yAccessor(d);
101
+ const x = xScale(xAccessor(d)) - 0.5 * candleWidth;
102
+ const y = yScale(Math.max(ohlc.open, ohlc.close));
103
+ const height = Math.abs(yScale(ohlc.open) - yScale(ohlc.close));
104
+
105
+ const fillStyle = d.fullyFormed ? (ohlc.open <= ohlc.close ? fill?.up : fill?.down) : fill?.partial;
106
+ const strokeStyle = d.fullyFormed ? (ohlc.open <= ohlc.close ? stroke?.up : stroke?.down) : undefined;
107
+
108
+ return {
109
+ fillStyle,
110
+ height,
111
+ strokeStyle,
112
+ width: candleWidth,
113
+ x,
114
+ y,
115
+ };
116
+ });
117
+ };
118
+ }
@@ -0,0 +1,111 @@
1
+ import * as React from "react";
2
+ import { first, getAxisCanvas, getMouseCanvas, GenericChartComponent, last } from "@tradingaction/core";
3
+
4
+ export interface SARSeriesProps {
5
+ readonly fillStyle?: {
6
+ falling: string;
7
+ rising: string;
8
+ };
9
+ readonly strokeStyle?: {
10
+ falling: string;
11
+ rising: string;
12
+ };
13
+ readonly highlightOnHover?: boolean;
14
+ readonly onClick?: (e: React.MouseEvent, moreProps: any) => void;
15
+ readonly onDoubleClick?: (e: React.MouseEvent, moreProps: any) => void;
16
+ readonly onContextMenu?: (e: React.MouseEvent, moreProps: any) => void;
17
+ readonly yAccessor: (datum: any) => number | undefined;
18
+ }
19
+
20
+ /**
21
+ * SAR stands for 'stop and reverse'.
22
+ * The indicator is below prices as they're rising and above
23
+ * prices as they're falling. In this regard, the indicator
24
+ * stops and reverses when the price trend reverses and breaks above or below the indicator.
25
+ */
26
+ export class SARSeries extends React.Component<SARSeriesProps> {
27
+ public static defaultProps = {
28
+ fillStyle: {
29
+ falling: "#4682B4",
30
+ rising: "#15EC2E",
31
+ },
32
+ highlightOnHover: false,
33
+ };
34
+
35
+ public render() {
36
+ const { highlightOnHover, onClick, onDoubleClick, onContextMenu } = this.props;
37
+
38
+ const hoverProps = highlightOnHover
39
+ ? {
40
+ isHover: this.isHover,
41
+ drawOn: ["mousemove", "pan"],
42
+ canvasToDraw: getMouseCanvas,
43
+ }
44
+ : {
45
+ drawOn: ["pan"],
46
+ canvasToDraw: getAxisCanvas,
47
+ };
48
+
49
+ return (
50
+ <GenericChartComponent
51
+ canvasDraw={this.drawOnCanvas}
52
+ onClickWhenHover={onClick}
53
+ onDoubleClickWhenHover={onDoubleClick}
54
+ onContextMenuWhenHover={onContextMenu}
55
+ {...hoverProps}
56
+ />
57
+ );
58
+ }
59
+
60
+ private readonly drawOnCanvas = (ctx: CanvasRenderingContext2D, moreProps: any) => {
61
+ const { yAccessor, fillStyle = SARSeries.defaultProps.fillStyle, strokeStyle } = this.props;
62
+ const {
63
+ xAccessor,
64
+ plotData,
65
+ xScale,
66
+ chartConfig: { yScale },
67
+ hovering,
68
+ } = moreProps;
69
+
70
+ const width = xScale(xAccessor(last(plotData))) - xScale(xAccessor(first(plotData)));
71
+
72
+ const d = ((width / plotData.length) * 0.5) / 2;
73
+ const radius = Math.min(2, Math.max(0.5, d)) + (hovering ? 2 : 0);
74
+
75
+ (plotData as any[]).forEach((each) => {
76
+ const yValue = yAccessor(each);
77
+ if (yValue === undefined) {
78
+ return;
79
+ }
80
+
81
+ const centerX = xScale(xAccessor(each));
82
+ const centerY = yScale(yValue);
83
+ const color = yValue > each.close ? fillStyle.falling : fillStyle.rising;
84
+
85
+ ctx.fillStyle = color;
86
+ if (strokeStyle !== undefined) {
87
+ ctx.strokeStyle = yValue > each.close ? strokeStyle.falling : strokeStyle.rising;
88
+ }
89
+
90
+ ctx.beginPath();
91
+ ctx.ellipse(centerX, centerY, radius, radius, 0, 0, 2 * Math.PI);
92
+ ctx.closePath();
93
+ ctx.fill();
94
+ if (strokeStyle !== undefined) {
95
+ ctx.stroke();
96
+ }
97
+ });
98
+ };
99
+
100
+ private readonly isHover = (moreProps: any) => {
101
+ const {
102
+ mouseXY,
103
+ currentItem,
104
+ chartConfig: { yScale },
105
+ } = moreProps;
106
+ const { yAccessor } = this.props;
107
+ const y = mouseXY[1];
108
+ const currentY = yScale(yAccessor(currentItem));
109
+ return y < currentY + 5 && y > currentY - 5;
110
+ };
111
+ }
@@ -0,0 +1,13 @@
1
+ import * as React from "react";
2
+ import { GenericChartComponent } from "@tradingaction/core";
3
+
4
+ interface SVGComponentProps {
5
+ readonly children: (moreProps: any) => React.ReactNode;
6
+ }
7
+
8
+ export class SVGComponent extends React.Component<SVGComponentProps> {
9
+ public render() {
10
+ const { children } = this.props;
11
+ return <GenericChartComponent drawOn={[]} svgDraw={children} />;
12
+ }
13
+ }