@operato/scene-scichart 2.0.0-beta.0
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.
- package/CHANGELOG.md +19 -0
- package/README.md +13 -0
- package/assets/favicon.ico +0 -0
- package/assets/images/spinner.png +0 -0
- package/db.sqlite +0 -0
- package/dist/charts/sci-candle-stick-chart.d.ts +19 -0
- package/dist/charts/sci-candle-stick-chart.js +248 -0
- package/dist/charts/sci-candle-stick-chart.js.map +1 -0
- package/dist/charts/volume-pallette-provider.d.ts +12 -0
- package/dist/charts/volume-pallette-provider.js +21 -0
- package/dist/charts/volume-pallette-provider.js.map +1 -0
- package/dist/data/binance-rest-client.d.ts +14 -0
- package/dist/data/binance-rest-client.js +53 -0
- package/dist/data/binance-rest-client.js.map +1 -0
- package/dist/editors/index.d.ts +0 -0
- package/dist/editors/index.js +2 -0
- package/dist/editors/index.js.map +1 -0
- package/dist/groups/index.d.ts +0 -0
- package/dist/groups/index.js +2 -0
- package/dist/groups/index.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/scichart-candle-stick.d.ts +10 -0
- package/dist/scichart-candle-stick.js +50 -0
- package/dist/scichart-candle-stick.js.map +1 -0
- package/dist/scichart-timeseries.d.ts +23 -0
- package/dist/scichart-timeseries.js +118 -0
- package/dist/scichart-timeseries.js.map +1 -0
- package/dist/scichart.d.ts +20 -0
- package/dist/scichart.js +70 -0
- package/dist/scichart.js.map +1 -0
- package/dist/templates/index.d.ts +14 -0
- package/dist/templates/index.js +4 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/scichart-candle-stick.d.ts +14 -0
- package/dist/templates/scichart-candle-stick.js +16 -0
- package/dist/templates/scichart-candle-stick.js.map +1 -0
- package/dist/templates/scichart.d.ts +14 -0
- package/dist/templates/scichart.js +16 -0
- package/dist/templates/scichart.js.map +1 -0
- package/dist/themes/app-theme.d.ts +56 -0
- package/dist/themes/app-theme.js +35 -0
- package/dist/themes/app-theme.js.map +1 -0
- package/icons/no-image.png +0 -0
- package/icons/scichart-candle-stick.png +0 -0
- package/icons/scichart.png +0 -0
- package/logs/.08636eb59927f12972f6774f5947c8507b3564c2-audit.json +25 -0
- package/logs/.5e5d741d8b7784a2fbad65eedc0fd46946aaf6f2-audit.json +30 -0
- package/logs/application-2024-03-15-04.log +28 -0
- package/logs/application-2024-03-17-03.log +12 -0
- package/logs/application-2024-03-17-04.log +6 -0
- package/logs/connections-2024-03-15-03.log +82 -0
- package/logs/connections-2024-03-15-04.log +164 -0
- package/logs/connections-2024-03-17-03.log +88 -0
- package/logs/connections-2024-03-17-04.log +44 -0
- package/package.json +62 -0
- package/schema.graphql +3969 -0
- package/src/charts/sci-candle-stick-chart.ts +305 -0
- package/src/charts/volume-pallette-provider.ts +41 -0
- package/src/data/binance-rest-client.ts +74 -0
- package/src/editors/index.ts +0 -0
- package/src/groups/index.ts +0 -0
- package/src/index.ts +2 -0
- package/src/scichart-candle-stick.ts +60 -0
- package/src/scichart-timeseries.ts +156 -0
- package/src/scichart.ts +88 -0
- package/src/templates/index.ts +4 -0
- package/src/templates/scichart-candle-stick.ts +16 -0
- package/src/templates/scichart.ts +16 -0
- package/src/themes/app-theme.ts +72 -0
- package/things-scene.config.js +9 -0
- package/translations/en.json +3 -0
- package/translations/ja.json +3 -0
- package/translations/ko.json +3 -0
- package/translations/ms.json +3 -0
- package/translations/zh.json +3 -0
- package/tsconfig.json +22 -0
- package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,305 @@
|
|
1
|
+
import { LitElement, PropertyValues, PropertyValueMap, css, html } from 'lit'
|
2
|
+
import { customElement, property, query, state } from 'lit/decorators.js'
|
3
|
+
|
4
|
+
import {
|
5
|
+
SciChartSurface,
|
6
|
+
NumberRange,
|
7
|
+
NumericAxis,
|
8
|
+
OhlcDataSeries,
|
9
|
+
FastCandlestickRenderableSeries,
|
10
|
+
ZoomPanModifier,
|
11
|
+
ZoomExtentsModifier,
|
12
|
+
MouseWheelZoomModifier,
|
13
|
+
ENumericFormat,
|
14
|
+
DateTimeNumericAxis,
|
15
|
+
EAutoRange,
|
16
|
+
FastLineRenderableSeries,
|
17
|
+
XyMovingAverageFilter,
|
18
|
+
SciChartOverview,
|
19
|
+
CursorModifier,
|
20
|
+
CursorTooltipSvgAnnotation,
|
21
|
+
SeriesInfo,
|
22
|
+
EDataSeriesType,
|
23
|
+
ESeriesType,
|
24
|
+
IRenderableSeries,
|
25
|
+
FastMountainRenderableSeries,
|
26
|
+
GradientParams,
|
27
|
+
Point,
|
28
|
+
OhlcSeriesInfo,
|
29
|
+
FastColumnRenderableSeries,
|
30
|
+
XyDataSeries,
|
31
|
+
FastOhlcRenderableSeries
|
32
|
+
} from 'scichart'
|
33
|
+
|
34
|
+
import { appTheme } from '../themes/app-theme'
|
35
|
+
import { VolumePaletteProvider } from './volume-pallette-provider'
|
36
|
+
import { simpleBinanceRestClient } from '../data/binance-rest-client'
|
37
|
+
|
38
|
+
const Y_AXIS_VOLUME_ID = 'Y_AXIS_VOLUME_ID'
|
39
|
+
|
40
|
+
SciChartSurface.configure({
|
41
|
+
dataUrl: `/node_modules/scichart/_wasm/scichart2d.data`,
|
42
|
+
wasmUrl: `/node_modules/scichart/_wasm/scichart2d.wasm`
|
43
|
+
})
|
44
|
+
|
45
|
+
@customElement('sci-candle-stock-chart')
|
46
|
+
export class SciCandleStockChart extends LitElement {
|
47
|
+
static styles = [
|
48
|
+
css`
|
49
|
+
:host {
|
50
|
+
display: flex;
|
51
|
+
flex-direction: column;
|
52
|
+
|
53
|
+
width: 100%;
|
54
|
+
}
|
55
|
+
|
56
|
+
#chart {
|
57
|
+
flex: 8;
|
58
|
+
}
|
59
|
+
|
60
|
+
#overview {
|
61
|
+
flex: 2;
|
62
|
+
}
|
63
|
+
`
|
64
|
+
]
|
65
|
+
|
66
|
+
render() {
|
67
|
+
return html`
|
68
|
+
<div id="chart"></div>
|
69
|
+
<div id="overview"></div>
|
70
|
+
`
|
71
|
+
}
|
72
|
+
|
73
|
+
@property({ type: String, attribute: 'series-type' }) seriesType: 'candle-stick' | 'ohlc' = 'candle-stick'
|
74
|
+
|
75
|
+
@state() candlestickChartSeries!: FastCandlestickRenderableSeries
|
76
|
+
@state() ohlcChartSeries!: FastOhlcRenderableSeries
|
77
|
+
@state() sciChartSurface?: SciChartSurface
|
78
|
+
@state() sciChartOverview?: SciChartOverview
|
79
|
+
|
80
|
+
@query('#chart') chart!: HTMLDivElement
|
81
|
+
@query('#overview') overview!: HTMLDivElement
|
82
|
+
|
83
|
+
disconnectedCallback(): void {
|
84
|
+
if (this.sciChartSurface) {
|
85
|
+
this.sciChartSurface.delete()
|
86
|
+
this.sciChartOverview!.delete()
|
87
|
+
this.sciChartSurface = undefined
|
88
|
+
this.sciChartOverview = undefined
|
89
|
+
return
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
protected async firstUpdated(_changedProperties: PropertyValueMap<any> | Map<PropertyKey, unknown>): Promise<void> {
|
94
|
+
const { sciChartSurface, overview, candlestickSeries, ohlcSeries } = await this.build()
|
95
|
+
|
96
|
+
this.candlestickChartSeries = candlestickSeries
|
97
|
+
this.ohlcChartSeries = ohlcSeries
|
98
|
+
this.sciChartSurface = sciChartSurface
|
99
|
+
this.sciChartOverview = overview
|
100
|
+
|
101
|
+
this.candlestickChartSeries.isVisible = this.seriesType == 'candle-stick'
|
102
|
+
this.ohlcChartSeries.isVisible = this.seriesType == 'ohlc'
|
103
|
+
}
|
104
|
+
|
105
|
+
updated(changes: PropertyValues<this>) {
|
106
|
+
if (changes.has('seriesType') && this.candlestickChartSeries) {
|
107
|
+
this.candlestickChartSeries.isVisible = this.seriesType == 'candle-stick'
|
108
|
+
this.ohlcChartSeries.isVisible = this.seriesType == 'ohlc'
|
109
|
+
}
|
110
|
+
}
|
111
|
+
|
112
|
+
// Override the Renderableseries to display on the scichart overview
|
113
|
+
private getOverviewSeries(defaultSeries: IRenderableSeries) {
|
114
|
+
if (defaultSeries.type === ESeriesType.CandlestickSeries) {
|
115
|
+
// Swap the default candlestick series on the overview chart for a mountain series. Same data
|
116
|
+
return new FastMountainRenderableSeries(defaultSeries.parentSurface.webAssemblyContext2D, {
|
117
|
+
dataSeries: defaultSeries.dataSeries,
|
118
|
+
fillLinearGradient: new GradientParams(new Point(0, 0), new Point(0, 1), [
|
119
|
+
{ color: appTheme.VividSkyBlue + '77', offset: 0 },
|
120
|
+
{ color: 'Transparent', offset: 1 }
|
121
|
+
]),
|
122
|
+
stroke: appTheme.VividSkyBlue
|
123
|
+
})
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
// Override the standard tooltip displayed by CursorModifier
|
128
|
+
private getTooltipLegendTemplate(seriesInfos: SeriesInfo[], svgAnnotation: CursorTooltipSvgAnnotation) {
|
129
|
+
let outputSvgString = ''
|
130
|
+
|
131
|
+
// Foreach series there will be a seriesInfo supplied by SciChart. This contains info about the series under the house
|
132
|
+
seriesInfos.forEach((seriesInfo, index) => {
|
133
|
+
const y = 20 + index * 20
|
134
|
+
const textColor = seriesInfo.stroke
|
135
|
+
let legendText = seriesInfo.formattedYValue
|
136
|
+
if (seriesInfo.dataSeriesType === EDataSeriesType.Ohlc) {
|
137
|
+
const o = seriesInfo as OhlcSeriesInfo
|
138
|
+
legendText = `Open=${o.formattedOpenValue} High=${o.formattedHighValue} Low=${o.formattedLowValue} Close=${o.formattedCloseValue}`
|
139
|
+
}
|
140
|
+
outputSvgString += `<text x="8" y="${y}" font-size="13" font-family="Verdana" fill="${textColor}">
|
141
|
+
${seriesInfo.seriesName}: ${legendText}
|
142
|
+
</text>`
|
143
|
+
})
|
144
|
+
|
145
|
+
return `<svg width="100%" height="100%">
|
146
|
+
${outputSvgString}
|
147
|
+
</svg>`
|
148
|
+
}
|
149
|
+
|
150
|
+
private async build() {
|
151
|
+
// Create a SciChartSurface
|
152
|
+
const { sciChartSurface, wasmContext } = await SciChartSurface.create(this.chart, {
|
153
|
+
theme: appTheme.SciChartJsTheme
|
154
|
+
})
|
155
|
+
|
156
|
+
// Add an XAxis of type DateTimeAxis
|
157
|
+
// Note for crypto data this is fine, but for stocks/forex you will need to use CategoryAxis which collapses gaps at weekends
|
158
|
+
// In future we have a hybrid IndexDateAxis which 'magically' solves problems of different # of points in stock market datasetd with gaps
|
159
|
+
const xAxis = new DateTimeNumericAxis(wasmContext, {
|
160
|
+
// autoRange.never as we're setting visibleRange explicitly below. If you dont do this, leave this flag default
|
161
|
+
autoRange: EAutoRange.Never
|
162
|
+
})
|
163
|
+
sciChartSurface.xAxes.add(xAxis)
|
164
|
+
|
165
|
+
// Create a NumericAxis on the YAxis with 2 Decimal Places
|
166
|
+
sciChartSurface.yAxes.add(
|
167
|
+
new NumericAxis(wasmContext, {
|
168
|
+
growBy: new NumberRange(0.1, 0.1),
|
169
|
+
labelFormat: ENumericFormat.Decimal,
|
170
|
+
labelPrecision: 2,
|
171
|
+
labelPrefix: '$',
|
172
|
+
autoRange: EAutoRange.Always
|
173
|
+
})
|
174
|
+
)
|
175
|
+
|
176
|
+
// Create a secondary YAxis to host volume data on its own scale
|
177
|
+
sciChartSurface.yAxes.add(
|
178
|
+
new NumericAxis(wasmContext, {
|
179
|
+
id: Y_AXIS_VOLUME_ID,
|
180
|
+
growBy: new NumberRange(0, 4),
|
181
|
+
isVisible: false,
|
182
|
+
autoRange: EAutoRange.Always
|
183
|
+
})
|
184
|
+
)
|
185
|
+
|
186
|
+
// Fetch data from now to 300 1hr candles ago
|
187
|
+
const endDate = new Date(Date.now())
|
188
|
+
const startDate = new Date()
|
189
|
+
startDate.setHours(endDate.getHours() - 300)
|
190
|
+
const priceBars = await simpleBinanceRestClient.getCandles('BTCUSDT', '1h', startDate, endDate)
|
191
|
+
|
192
|
+
// Maps PriceBar { date, open, high, low, close, volume } to structure-of-arrays expected by scichart
|
193
|
+
const xValues: number[] = []
|
194
|
+
const openValues: number[] = []
|
195
|
+
const highValues: number[] = []
|
196
|
+
const lowValues: number[] = []
|
197
|
+
const closeValues: number[] = []
|
198
|
+
const volumeValues: number[] = []
|
199
|
+
priceBars.forEach((priceBar: any) => {
|
200
|
+
xValues.push(priceBar.date)
|
201
|
+
openValues.push(priceBar.open)
|
202
|
+
highValues.push(priceBar.high)
|
203
|
+
lowValues.push(priceBar.low)
|
204
|
+
closeValues.push(priceBar.close)
|
205
|
+
volumeValues.push(priceBar.volume)
|
206
|
+
})
|
207
|
+
|
208
|
+
// Zoom to the latest 100 candles
|
209
|
+
const startViewportRange = new Date()
|
210
|
+
startViewportRange.setHours(startDate.getHours() - 100)
|
211
|
+
xAxis.visibleRange = new NumberRange(startViewportRange.getTime() / 1000, endDate.getTime() / 1000)
|
212
|
+
|
213
|
+
// Create and add the Candlestick series
|
214
|
+
// The Candlestick Series requires a special dataseries type called OhlcDataSeries with o,h,l,c and date values
|
215
|
+
const candleDataSeries = new OhlcDataSeries(wasmContext, {
|
216
|
+
xValues,
|
217
|
+
openValues,
|
218
|
+
highValues,
|
219
|
+
lowValues,
|
220
|
+
closeValues,
|
221
|
+
dataSeriesName: 'BTC/USDT'
|
222
|
+
})
|
223
|
+
const candlestickSeries = new FastCandlestickRenderableSeries(wasmContext, {
|
224
|
+
dataSeries: candleDataSeries,
|
225
|
+
stroke: appTheme.ForegroundColor, // used by cursorModifier below
|
226
|
+
strokeThickness: 1,
|
227
|
+
brushUp: appTheme.VividGreen + '77',
|
228
|
+
brushDown: appTheme.MutedRed + '77',
|
229
|
+
strokeUp: appTheme.VividGreen,
|
230
|
+
strokeDown: appTheme.MutedRed
|
231
|
+
})
|
232
|
+
sciChartSurface.renderableSeries.add(candlestickSeries)
|
233
|
+
|
234
|
+
// Add an Ohlcseries. this will be invisible to begin with
|
235
|
+
const ohlcSeries = new FastOhlcRenderableSeries(wasmContext, {
|
236
|
+
dataSeries: candleDataSeries,
|
237
|
+
stroke: appTheme.ForegroundColor, // used by cursorModifier below
|
238
|
+
strokeThickness: 1,
|
239
|
+
dataPointWidth: 0.9,
|
240
|
+
strokeUp: appTheme.VividGreen,
|
241
|
+
strokeDown: appTheme.MutedRed,
|
242
|
+
isVisible: false
|
243
|
+
})
|
244
|
+
sciChartSurface.renderableSeries.add(ohlcSeries)
|
245
|
+
|
246
|
+
// Add some moving averages using SciChart's filters/transforms API
|
247
|
+
// when candleDataSeries updates, XyMovingAverageFilter automatically recomputes
|
248
|
+
sciChartSurface.renderableSeries.add(
|
249
|
+
new FastLineRenderableSeries(wasmContext, {
|
250
|
+
dataSeries: new XyMovingAverageFilter(candleDataSeries, {
|
251
|
+
dataSeriesName: 'Moving Average (20)',
|
252
|
+
length: 20
|
253
|
+
}),
|
254
|
+
stroke: appTheme.VividSkyBlue
|
255
|
+
})
|
256
|
+
)
|
257
|
+
|
258
|
+
sciChartSurface.renderableSeries.add(
|
259
|
+
new FastLineRenderableSeries(wasmContext, {
|
260
|
+
dataSeries: new XyMovingAverageFilter(candleDataSeries, {
|
261
|
+
dataSeriesName: 'Moving Average (50)',
|
262
|
+
length: 50
|
263
|
+
}),
|
264
|
+
stroke: appTheme.VividPink
|
265
|
+
})
|
266
|
+
)
|
267
|
+
|
268
|
+
// Add volume data onto the chart
|
269
|
+
sciChartSurface.renderableSeries.add(
|
270
|
+
new FastColumnRenderableSeries(wasmContext, {
|
271
|
+
dataSeries: new XyDataSeries(wasmContext, { xValues, yValues: volumeValues, dataSeriesName: 'Volume' }),
|
272
|
+
strokeThickness: 0,
|
273
|
+
// This is how we get volume to scale - on a hidden YAxis
|
274
|
+
yAxisId: Y_AXIS_VOLUME_ID,
|
275
|
+
// This is how we colour volume bars red or green
|
276
|
+
paletteProvider: new VolumePaletteProvider(
|
277
|
+
candleDataSeries,
|
278
|
+
appTheme.VividGreen + '77',
|
279
|
+
appTheme.MutedRed + '77'
|
280
|
+
)
|
281
|
+
})
|
282
|
+
)
|
283
|
+
|
284
|
+
// Optional: Add some interactivity modifiers
|
285
|
+
sciChartSurface.chartModifiers.add(
|
286
|
+
new ZoomExtentsModifier(),
|
287
|
+
new ZoomPanModifier(),
|
288
|
+
new MouseWheelZoomModifier(),
|
289
|
+
new CursorModifier({
|
290
|
+
crosshairStroke: appTheme.VividOrange,
|
291
|
+
axisLabelFill: appTheme.VividOrange,
|
292
|
+
tooltipLegendTemplate: this.getTooltipLegendTemplate
|
293
|
+
})
|
294
|
+
)
|
295
|
+
|
296
|
+
// Add Overview chart. This will automatically bind to the parent surface
|
297
|
+
// displaying its series. Zooming the chart will zoom the overview and vice versa
|
298
|
+
const overview = await SciChartOverview.create(sciChartSurface, this.overview, {
|
299
|
+
theme: appTheme.SciChartJsTheme,
|
300
|
+
transformRenderableSeries: this.getOverviewSeries as any
|
301
|
+
})
|
302
|
+
|
303
|
+
return { sciChartSurface, overview, candlestickSeries, ohlcSeries }
|
304
|
+
}
|
305
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import {
|
2
|
+
OhlcDataSeries,
|
3
|
+
IRenderableSeries,
|
4
|
+
EFillPaletteMode,
|
5
|
+
IFillPaletteProvider,
|
6
|
+
IPointMetadata,
|
7
|
+
parseColorToUIntArgb
|
8
|
+
} from 'scichart'
|
9
|
+
|
10
|
+
export class VolumePaletteProvider implements IFillPaletteProvider {
|
11
|
+
fillPaletteMode: EFillPaletteMode = EFillPaletteMode.SOLID
|
12
|
+
private ohlcDataSeries: OhlcDataSeries
|
13
|
+
private upColorArgb: number
|
14
|
+
private downColorArgb: number
|
15
|
+
|
16
|
+
constructor(masterData: OhlcDataSeries, upColor: string, downColor: string) {
|
17
|
+
this.upColorArgb = parseColorToUIntArgb(upColor)
|
18
|
+
this.downColorArgb = parseColorToUIntArgb(downColor)
|
19
|
+
this.ohlcDataSeries = masterData
|
20
|
+
}
|
21
|
+
onAttached(parentSeries: IRenderableSeries): void {}
|
22
|
+
onDetached(): void {}
|
23
|
+
|
24
|
+
// Return up or down color for the volume bars depending on Ohlc data
|
25
|
+
overrideFillArgb(xValue: number, yValue: number, index: number, opacity?: number, metadata?: IPointMetadata): number {
|
26
|
+
const isUpCandle =
|
27
|
+
this.ohlcDataSeries.getNativeOpenValues().get(index) >= this.ohlcDataSeries.getNativeCloseValues().get(index)
|
28
|
+
return isUpCandle ? this.upColorArgb : this.downColorArgb
|
29
|
+
}
|
30
|
+
|
31
|
+
// Override stroke as well, even though strokethickness is 0, because stroke is used if column thickness goes to 0.
|
32
|
+
overrideStrokeArgb(
|
33
|
+
xValue: number,
|
34
|
+
yValue: number,
|
35
|
+
index: number,
|
36
|
+
opacity?: number,
|
37
|
+
metadata?: IPointMetadata
|
38
|
+
): number {
|
39
|
+
return this.overrideFillArgb(xValue, yValue, index, opacity, metadata)
|
40
|
+
}
|
41
|
+
}
|
@@ -0,0 +1,74 @@
|
|
1
|
+
/**
|
2
|
+
* Defines a price bar with Open, High, Low, Close and Date encoded as number
|
3
|
+
*/
|
4
|
+
export type TPriceBar = {
|
5
|
+
date: number
|
6
|
+
open: number
|
7
|
+
high: number
|
8
|
+
low: number
|
9
|
+
close: number
|
10
|
+
volume: number
|
11
|
+
}
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Parses JSON candles into TPriceBar array
|
15
|
+
* @param candles
|
16
|
+
*/
|
17
|
+
const parseCandles = (candles: any[]): TPriceBar[] => {
|
18
|
+
const priceBars: TPriceBar[] = []
|
19
|
+
|
20
|
+
candles.forEach((candle: any) => {
|
21
|
+
const [timestamp, open, high, low, close, volume] = candle
|
22
|
+
const openValue = parseFloat(open)
|
23
|
+
const highValue = parseFloat(high)
|
24
|
+
const lowValue = parseFloat(low)
|
25
|
+
const closeValue = parseFloat(close)
|
26
|
+
const volumeValue = parseFloat(volume)
|
27
|
+
|
28
|
+
priceBars.push({
|
29
|
+
date: timestamp / 1000,
|
30
|
+
open: openValue,
|
31
|
+
high: highValue,
|
32
|
+
low: lowValue,
|
33
|
+
close: closeValue,
|
34
|
+
volume: volumeValue
|
35
|
+
})
|
36
|
+
})
|
37
|
+
|
38
|
+
return priceBars
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Fetches candles from Binance Rest API
|
43
|
+
*/
|
44
|
+
const getCandles = async (
|
45
|
+
symbol: string,
|
46
|
+
interval: string,
|
47
|
+
startTime?: Date,
|
48
|
+
endTime?: Date,
|
49
|
+
limit: number = 500
|
50
|
+
): Promise<TPriceBar[]> => {
|
51
|
+
let url = `https://api.binance.us/api/v3/klines?symbol=${symbol}&interval=${interval}`
|
52
|
+
if (startTime) {
|
53
|
+
url += `&startTime=${startTime.getTime()}`
|
54
|
+
}
|
55
|
+
if (endTime) {
|
56
|
+
url += `&endTime=${endTime.getTime()}`
|
57
|
+
}
|
58
|
+
if (limit) {
|
59
|
+
url += `&limit=${limit}`
|
60
|
+
}
|
61
|
+
try {
|
62
|
+
console.log(`SimpleBinanceClient: Fetching ${symbol} ${interval} from ${startTime} to ${endTime}`)
|
63
|
+
const response = await fetch(url)
|
64
|
+
const data = await response.json()
|
65
|
+
return parseCandles(data)
|
66
|
+
} catch (err) {
|
67
|
+
console.error(err)
|
68
|
+
return []
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
export const simpleBinanceRestClient = {
|
73
|
+
getCandles
|
74
|
+
}
|
File without changes
|
File without changes
|
package/src/index.ts
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright © HatioLab Inc. All rights reserved.
|
3
|
+
*/
|
4
|
+
|
5
|
+
const NATURE = {
|
6
|
+
mutable: false,
|
7
|
+
resizable: true,
|
8
|
+
rotatable: true,
|
9
|
+
properties: [
|
10
|
+
{
|
11
|
+
type: 'select',
|
12
|
+
label: 'series',
|
13
|
+
name: 'series',
|
14
|
+
property: {
|
15
|
+
options: ['', 'candle-stick', 'ohlc']
|
16
|
+
}
|
17
|
+
}
|
18
|
+
]
|
19
|
+
}
|
20
|
+
|
21
|
+
import './charts/sci-candle-stick-chart'
|
22
|
+
import { Component, HTMLOverlayContainer, Properties, ComponentNature, error } from '@hatiolab/things-scene'
|
23
|
+
|
24
|
+
import { SciCandleStockChart } from './charts/sci-candle-stick-chart'
|
25
|
+
|
26
|
+
export default class ScichartCandleStick extends HTMLOverlayContainer {
|
27
|
+
static get nature(): ComponentNature {
|
28
|
+
return NATURE
|
29
|
+
}
|
30
|
+
|
31
|
+
dispose() {
|
32
|
+
super.dispose()
|
33
|
+
}
|
34
|
+
|
35
|
+
/*
|
36
|
+
* 컴포넌트의 생성 또는 속성 변화 시에 호출되며,
|
37
|
+
* 그에 따른 html element의 반영이 필요한 부분을 구현한다.
|
38
|
+
*
|
39
|
+
* ThingsComponent state => HTML element properties
|
40
|
+
*/
|
41
|
+
setElementProperties(chart: SciCandleStockChart) {
|
42
|
+
const { series = 'candle-stick' } = this.state
|
43
|
+
|
44
|
+
chart.seriesType = series
|
45
|
+
}
|
46
|
+
|
47
|
+
/*
|
48
|
+
* 컴포넌트가 ready 상태가 되거나, 컴포넌트의 속성이 변화될 시 setElementProperties 뒤에 호출된다.
|
49
|
+
* 변화에 따른 기본적인 html 속성이 super.reposition()에서 진행되고, 그 밖의 작업이 필요할 때, 오버라이드 한다.
|
50
|
+
*/
|
51
|
+
reposition() {
|
52
|
+
super.reposition()
|
53
|
+
}
|
54
|
+
|
55
|
+
get tagName() {
|
56
|
+
return 'sci-candle-stock-chart'
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
Component.register('scichart-candle-stick', ScichartCandleStick)
|
@@ -0,0 +1,156 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright © HatioLab Inc. All rights reserved.
|
3
|
+
*/
|
4
|
+
|
5
|
+
const NATURE = {
|
6
|
+
mutable: false,
|
7
|
+
resizable: true,
|
8
|
+
rotatable: true,
|
9
|
+
properties: [
|
10
|
+
{
|
11
|
+
type: 'number',
|
12
|
+
label: 'sample-count',
|
13
|
+
name: 'sampleCount'
|
14
|
+
},
|
15
|
+
{
|
16
|
+
type: 'number',
|
17
|
+
label: 'max-value',
|
18
|
+
name: 'maxValue'
|
19
|
+
},
|
20
|
+
{
|
21
|
+
type: 'string',
|
22
|
+
label: 'attr-x',
|
23
|
+
name: 'attrX'
|
24
|
+
},
|
25
|
+
{
|
26
|
+
type: 'string',
|
27
|
+
label: 'attr-y',
|
28
|
+
name: 'attrY'
|
29
|
+
}
|
30
|
+
]
|
31
|
+
}
|
32
|
+
|
33
|
+
import { Component, HTMLOverlayContainer, Properties, ComponentNature, error } from '@hatiolab/things-scene'
|
34
|
+
|
35
|
+
import {
|
36
|
+
MouseWheelZoomModifier,
|
37
|
+
FastLineRenderableSeries,
|
38
|
+
XyDataSeries,
|
39
|
+
ZoomExtentsModifier,
|
40
|
+
ZoomPanModifier,
|
41
|
+
NumberRange,
|
42
|
+
EAxisAlignment,
|
43
|
+
EAutoRange
|
44
|
+
} from 'scichart'
|
45
|
+
|
46
|
+
import { SciChartSurface } from 'scichart/Charting/Visuals/SciChartSurface'
|
47
|
+
import { NumericAxis } from 'scichart/Charting/Visuals/Axis/NumericAxis'
|
48
|
+
|
49
|
+
SciChartSurface.configure({
|
50
|
+
dataUrl: `/node_modules/scichart/_wasm/scichart2d.data`,
|
51
|
+
wasmUrl: `/node_modules/scichart/_wasm/scichart2d.wasm`
|
52
|
+
})
|
53
|
+
|
54
|
+
export default class ScichartTimeSeries extends HTMLOverlayContainer {
|
55
|
+
static get nature() {
|
56
|
+
return NATURE
|
57
|
+
}
|
58
|
+
|
59
|
+
_anchor?: HTMLDivElement
|
60
|
+
|
61
|
+
async oncreate_element(div: HTMLDivElement) {
|
62
|
+
const { sciChartSurface, wasmContext } = await SciChartSurface.create(div)
|
63
|
+
var { sampleCount, maxValue } = this.state
|
64
|
+
|
65
|
+
// SciChartSurface.setRuntimeLicenseKey('YOUR_RUNTIME_KEY')
|
66
|
+
|
67
|
+
const xAxis = new NumericAxis(wasmContext, {
|
68
|
+
axisTitle: 'X Axis',
|
69
|
+
autoRange: EAutoRange.Never,
|
70
|
+
axisAlignment: EAxisAlignment.Bottom,
|
71
|
+
visibleRange: new NumberRange(0, sampleCount)
|
72
|
+
})
|
73
|
+
const yAxis = new NumericAxis(wasmContext, {
|
74
|
+
axisTitle: 'Y Axis',
|
75
|
+
autoRange: EAutoRange.Never,
|
76
|
+
visibleRange: new NumberRange(0, maxValue)
|
77
|
+
})
|
78
|
+
|
79
|
+
sciChartSurface.xAxes.add(xAxis)
|
80
|
+
sciChartSurface.yAxes.add(yAxis)
|
81
|
+
|
82
|
+
const dataSeries = new XyDataSeries(wasmContext, {
|
83
|
+
xValues: this.dataSet.map(d => d.xValue),
|
84
|
+
yValues: this.dataSet.map(d => d.yValue)
|
85
|
+
})
|
86
|
+
|
87
|
+
const lineSeries = new FastLineRenderableSeries(wasmContext, {
|
88
|
+
dataSeries: dataSeries,
|
89
|
+
xAxisId: sciChartSurface.xAxes.get(0).id,
|
90
|
+
yAxisId: sciChartSurface.yAxes.get(0).id,
|
91
|
+
stroke: '#FF6600',
|
92
|
+
strokeThickness: 2
|
93
|
+
})
|
94
|
+
|
95
|
+
sciChartSurface.renderableSeries.add(lineSeries)
|
96
|
+
|
97
|
+
// ZoomPanModifier, MouseWheelZoomModifier, ZoomExtentsModifier 추가
|
98
|
+
// ZoomPanModifier: Chart를 드래그하여 이동
|
99
|
+
// MouseWheelZoomModifier: 마우스 휠로 Chart Zoom
|
100
|
+
// ZoomExtentsModifier: Chart의 전체 범위로 Zoom
|
101
|
+
sciChartSurface.chartModifiers.add(
|
102
|
+
new ZoomPanModifier({ enableZoom: true }),
|
103
|
+
new MouseWheelZoomModifier(),
|
104
|
+
new ZoomExtentsModifier()
|
105
|
+
)
|
106
|
+
}
|
107
|
+
|
108
|
+
dispose() {
|
109
|
+
super.dispose()
|
110
|
+
|
111
|
+
delete this._anchor
|
112
|
+
}
|
113
|
+
|
114
|
+
/*
|
115
|
+
* 컴포넌트의 생성 또는 속성 변화 시에 호출되며,
|
116
|
+
* 그에 따른 html element의 반영이 필요한 부분을 구현한다.
|
117
|
+
*
|
118
|
+
* ThingsComponent state => HTML element properties
|
119
|
+
*/
|
120
|
+
setElementProperties(div: HTMLDivElement) {}
|
121
|
+
|
122
|
+
/*
|
123
|
+
* 컴포넌트가 ready 상태가 되거나, 컴포넌트의 속성이 변화될 시 setElementProperties 뒤에 호출된다.
|
124
|
+
* 변화에 따른 기본적인 html 속성이 super.reposition()에서 진행되고, 그 밖의 작업이 필요할 때, 오버라이드 한다.
|
125
|
+
*/
|
126
|
+
reposition() {
|
127
|
+
super.reposition()
|
128
|
+
}
|
129
|
+
|
130
|
+
get dataSet(): { xValue: number; yValue: number }[] {
|
131
|
+
var { attrX, attrY, data } = this.state
|
132
|
+
|
133
|
+
if (!(data instanceof Array)) {
|
134
|
+
return []
|
135
|
+
}
|
136
|
+
|
137
|
+
return data
|
138
|
+
.map(item => {
|
139
|
+
if (!item || typeof item !== 'object') {
|
140
|
+
return
|
141
|
+
}
|
142
|
+
|
143
|
+
return {
|
144
|
+
xValue: item[attrX],
|
145
|
+
yValue: item[attrY]
|
146
|
+
}
|
147
|
+
})
|
148
|
+
.filter(Boolean) as { xValue: number; yValue: number }[]
|
149
|
+
}
|
150
|
+
|
151
|
+
get tagName() {
|
152
|
+
return 'div'
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
Component.register('scichart-timeseries', ScichartTimeSeries)
|