chart-library-native 1.0.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/src/README.md ADDED
@@ -0,0 +1,241 @@
1
+ # 📚 High-Level API Wrapper - Usage Guide
2
+
3
+ ## 🎯 Two-Tier API Architecture
4
+
5
+ ### **Tier 1: Simplified API** ⭐ (90% use cases)
6
+ - Zero configuration
7
+ - Pre-configured defaults
8
+ - Built-in signal calculations
9
+ - Ready-to-use components
10
+
11
+ ### **Tier 2: Advanced API** (10% use cases)
12
+ - Full customization
13
+ - Custom parameters
14
+ - Direct control
15
+
16
+ ---
17
+
18
+ ## 🚀 Quick Start (Tier 1)
19
+
20
+ ### Example 1: Simple MA
21
+
22
+ ```typescript
23
+ import { useQuickMA, QuickIndicatorDisplay } from 'chart-library-native/src';
24
+
25
+ const MyComponent = () => {
26
+ const closes = [100, 101, 102, 101, 100, 99, 98, 99, 100, 101];
27
+ const { data, lastValues, loading } = useQuickMA(closes);
28
+
29
+ return (
30
+ <View>
31
+ <QuickIndicatorDisplay
32
+ name="MA20"
33
+ value={lastValues.current}
34
+ previousValue={lastValues.previous}
35
+ />
36
+ <Text>Trend: {lastValues.trend}</Text>
37
+ </View>
38
+ );
39
+ };
40
+ ```
41
+
42
+ ### Example 2: Multiple Indicators
43
+
44
+ ```typescript
45
+ import {
46
+ useQuickMA,
47
+ useQuickRSI,
48
+ useQuickMACD,
49
+ QuickIndicatorDisplay
50
+ } from 'chart-library-native/src';
51
+
52
+ const Dashboard = () => {
53
+ const closes = [100, 101, 102, 101, 100, 99, 98, 99, 100, 101];
54
+
55
+ const ma = useQuickMA(closes);
56
+ const rsi = useQuickRSI(closes);
57
+ const macd = useQuickMACD(closes);
58
+
59
+ return (
60
+ <ScrollView>
61
+ <QuickIndicatorDisplay
62
+ name="MA20"
63
+ value={ma.lastValues.current}
64
+ />
65
+
66
+ <QuickIndicatorDisplay
67
+ name="RSI14"
68
+ value={rsi.currentLevel.value}
69
+ color={rsi.currentLevel.level === 'overbought' ? '#f44336' : '#4CAF50'}
70
+ />
71
+
72
+ <View>
73
+ <Text>MACD Signal: {macd.currentSignal.signal}</Text>
74
+ </View>
75
+ </ScrollView>
76
+ );
77
+ };
78
+ ```
79
+
80
+ ---
81
+
82
+ ## 🔧 Advanced Usage (Tier 2)
83
+
84
+ ### Custom MA Period
85
+
86
+ ```typescript
87
+ import { useMA } from 'chart-library-native/src';
88
+
89
+ const CustomMA = () => {
90
+ const closes = [100, 101, 102, 101, 100, 99, 98, 99, 100, 101];
91
+
92
+ // Custom period: MA50
93
+ const { data, loading } = useMA(closes, {
94
+ period: 50,
95
+ type: 'simple'
96
+ });
97
+
98
+ return <Text>MA50: {data[data.length - 1]}</Text>;
99
+ };
100
+ ```
101
+
102
+ ### Custom MACD Settings
103
+
104
+ ```typescript
105
+ import { useMACD } from 'chart-library-native/src';
106
+
107
+ const CustomMACD = () => {
108
+ const closes = [100, 101, 102, 101, 100, 99, 98, 99, 100, 101];
109
+
110
+ // Custom MACD: fast=8, slow=21, signal=5
111
+ const { macd, signal, histogram } = useMACD(closes, {
112
+ fast: 8,
113
+ slow: 21,
114
+ signal: 5
115
+ });
116
+
117
+ return (
118
+ <View>
119
+ <Text>MACD: {macd[macd.length - 1]}</Text>
120
+ <Text>Signal: {signal[signal.length - 1]}</Text>
121
+ </View>
122
+ );
123
+ };
124
+ ```
125
+
126
+ ---
127
+
128
+ ## 📊 Available Tier 1 Hooks
129
+
130
+ | Hook | Default | Description |
131
+ |------|---------|-------------|
132
+ | `useQuickMA` | MA20 | Simple Moving Average |
133
+ | `useQuickEMA` | EMA12 | Exponential Moving Average |
134
+ | `useQuickMACD` | 12/26/9 | MACD with signal calculation |
135
+ | `useQuickRSI` | RSI14 | RSI with overbought/oversold levels |
136
+ | `useQuickKDJ` | 9/3/3 | KDJ Stochastic |
137
+ | `useQuickBollinger` | BB(20,2) | Bollinger Bands |
138
+ | `useQuickDMI` | DMI(14) | Directional Movement Index |
139
+
140
+ ---
141
+
142
+ ## 🎨 Components
143
+
144
+ ### QuickIndicatorDisplay
145
+
146
+ Pre-built component to display indicator values:
147
+
148
+ ```typescript
149
+ <QuickIndicatorDisplay
150
+ name="MA20"
151
+ value={106.5}
152
+ previousValue={105.8}
153
+ decimals={2}
154
+ color="#1f77b4"
155
+ status="success"
156
+ />
157
+ ```
158
+
159
+ **Props:**
160
+ - `name`: Indicator name
161
+ - `value`: Current value
162
+ - `previousValue`: Previous value (optional, for trend calculation)
163
+ - `decimals`: Number of decimal places (default: 2)
164
+ - `color`: Text color (default: '#1f77b4')
165
+ - `status`: 'success' | 'error'
166
+ - `error`: Error message (if status is 'error')
167
+
168
+ ---
169
+
170
+ ## 📝 Migration: Tier 1 → Tier 2
171
+
172
+ ```typescript
173
+ // Start with Tier 1
174
+ import { useQuickMA } from 'chart-library-native/src';
175
+ const { data } = useQuickMA(closes);
176
+
177
+ // Later, migrate to Tier 2 for custom period
178
+ import { useMA } from 'chart-library-native/src';
179
+ const { data } = useMA(closes, { period: 50, type: 'simple' });
180
+
181
+ // Zero breaking changes!
182
+ ```
183
+
184
+ ---
185
+
186
+ ## 🔗 Architecture
187
+
188
+ ```
189
+ ┌─────────────────────────────────┐
190
+ │ Tier 1: Simplified API │
191
+ │ useQuickMA, useQuickRSI, etc. │
192
+ └──────────────┬──────────────────┘
193
+
194
+ ┌──────────────▼──────────────────┐
195
+ │ Tier 2: Advanced API │
196
+ │ useMA, useRSI, useMACD, etc. │
197
+ └──────────────┬──────────────────┘
198
+
199
+ ┌──────────────▼──────────────────┐
200
+ │ Low-Level: ChartLibrary │
201
+ │ Direct JSI calls │
202
+ └──────────────┬──────────────────┘
203
+
204
+ ┌──────────────▼──────────────────┐
205
+ │ C API Layer │
206
+ │ chart_calculate_ma, etc. │
207
+ └──────────────┬──────────────────┘
208
+
209
+ ┌──────────────▼──────────────────┐
210
+ │ C++ Core │
211
+ │ calculateMA, calculateRSI, etc│
212
+ └─────────────────────────────────┘
213
+ ```
214
+
215
+ ---
216
+
217
+ ## ✅ Benefits
218
+
219
+ ### Tier 1 Benefits:
220
+ - ✅ Zero configuration
221
+ - ✅ Industry-standard defaults
222
+ - ✅ Built-in signal calculations
223
+ - ✅ Ready-to-use components
224
+ - ✅ Perfect for 90% of use cases
225
+
226
+ ### Tier 2 Benefits:
227
+ - ✅ Full customization
228
+ - ✅ Any parameters
229
+ - ✅ Advanced features
230
+ - ✅ Direct control
231
+
232
+ ---
233
+
234
+ ## 📚 More Examples
235
+
236
+ See `examples/` directory for complete examples:
237
+ - Simple dashboard
238
+ - Multiple indicators
239
+ - Custom configurations
240
+ - Chart integration
241
+
@@ -0,0 +1,109 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/components/QuickIndicatorDisplay.tsx
3
+ // Pre-built component to display any indicator
4
+ // ============================================================================
5
+
6
+ import React from 'react';
7
+ import { View, Text, StyleSheet } from 'react-native';
8
+
9
+ interface QuickIndicatorDisplayProps {
10
+ name: string;
11
+ value: number | null;
12
+ previousValue?: number | null;
13
+ decimals?: number;
14
+ color?: string;
15
+ status?: 'success' | 'error';
16
+ error?: string;
17
+ }
18
+
19
+ /**
20
+ * Pre-built indicator display component
21
+ *
22
+ * @example
23
+ * <QuickIndicatorDisplay
24
+ * name="MA20"
25
+ * value={106.5}
26
+ * previousValue={105.8}
27
+ * status="success"
28
+ * />
29
+ */
30
+ export const QuickIndicatorDisplay: React.FC<QuickIndicatorDisplayProps> = ({
31
+ name,
32
+ value,
33
+ previousValue,
34
+ decimals = 2,
35
+ color = '#1f77b4',
36
+ status = 'success',
37
+ error
38
+ }) => {
39
+ const formattedValue = value !== null ? value.toFixed(decimals) : 'N/A';
40
+ const change =
41
+ value !== null && previousValue !== null && previousValue !== undefined
42
+ ? ((value - previousValue) / Math.abs(previousValue) * 100).toFixed(2)
43
+ : null;
44
+
45
+ const changeColor = change && parseFloat(change) > 0 ? '#4CAF50' : '#f44336';
46
+
47
+ return (
48
+ <View style={styles.container}>
49
+ <View style={styles.header}>
50
+ <Text style={styles.name}>{name}</Text>
51
+ {status === 'error' && (
52
+ <Text style={styles.error}>❌ {error || 'Error'}</Text>
53
+ )}
54
+ </View>
55
+
56
+ <View style={styles.content}>
57
+ <Text style={[styles.value, { color }]}>
58
+ {formattedValue}
59
+ </Text>
60
+
61
+ {change !== null && (
62
+ <Text style={[styles.change, { color: changeColor }]}>
63
+ {parseFloat(change) > 0 ? '↑' : '↓'} {change}%
64
+ </Text>
65
+ )}
66
+ </View>
67
+ </View>
68
+ );
69
+ };
70
+
71
+ const styles = StyleSheet.create({
72
+ container: {
73
+ padding: 12,
74
+ backgroundColor: '#fff',
75
+ borderRadius: 8,
76
+ marginVertical: 6,
77
+ borderLeftWidth: 4,
78
+ borderLeftColor: '#1f77b4'
79
+ },
80
+ header: {
81
+ flexDirection: 'row',
82
+ justifyContent: 'space-between',
83
+ alignItems: 'center'
84
+ },
85
+ name: {
86
+ fontSize: 12,
87
+ fontWeight: '600',
88
+ color: '#666'
89
+ },
90
+ error: {
91
+ fontSize: 10,
92
+ color: '#f44336'
93
+ },
94
+ content: {
95
+ flexDirection: 'row',
96
+ justifyContent: 'space-between',
97
+ alignItems: 'baseline',
98
+ marginTop: 8
99
+ },
100
+ value: {
101
+ fontSize: 18,
102
+ fontWeight: 'bold'
103
+ },
104
+ change: {
105
+ fontSize: 12,
106
+ fontWeight: '600'
107
+ }
108
+ });
109
+
@@ -0,0 +1,16 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/components/index.ts
3
+ // Pre-built components ready to use
4
+ // ============================================================================
5
+
6
+ export { QuickIndicatorDisplay } from './QuickIndicatorDisplay';
7
+ // Note: Chart components (SimpleLineChart, MAChart, etc.) would require
8
+ // react-native-chart-kit or similar library. They are placeholders for now.
9
+ // export { SimpleLineChart } from './SimpleLineChart';
10
+ // export { MAChart } from './MAChart';
11
+ // export { MACDChart } from './MACDChart';
12
+ // export { RSIChart } from './RSIChart';
13
+ // export { KDJChart } from './KDJChart';
14
+ // export { BollingerChart } from './BollingerChart';
15
+ // export { ChartCard } from './ChartCard';
16
+
@@ -0,0 +1,15 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/index.ts
3
+ // Tier 1 Simplified API exports
4
+ // ============================================================================
5
+
6
+ export { useQuickMA } from './useQuickMA';
7
+ export { useQuickEMA } from './useQuickEMA';
8
+ export { useQuickMACD } from './useQuickMACD';
9
+ export { useQuickRSI } from './useQuickRSI';
10
+ export { useQuickKDJ } from './useQuickKDJ';
11
+ export { useQuickBollinger } from './useQuickBollinger';
12
+ export { useQuickDMI } from './useQuickDMI';
13
+
14
+ export * from './components';
15
+
@@ -0,0 +1,58 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/useQuickBollinger.ts
3
+ // Tier 1 API: Bollinger Bands
4
+ // ============================================================================
5
+
6
+ import { useBollinger } from '../../hooks/useIndicators';
7
+
8
+ export interface UseQuickBollingerResult {
9
+ upper: (number | null)[];
10
+ middle: (number | null)[];
11
+ lower: (number | null)[];
12
+ loading: boolean;
13
+ error?: string;
14
+ currentBands: {
15
+ upper: number | null;
16
+ middle: number | null;
17
+ lower: number | null;
18
+ price: number | null;
19
+ position: 'above' | 'within' | 'below';
20
+ };
21
+ status: 'success' | 'error';
22
+ }
23
+
24
+ /**
25
+ * Quick Bollinger Bands Hook - Standard BB(20,2)
26
+ *
27
+ * @param closes - Array of close prices
28
+ * @returns { upper, middle, lower, currentBands, loading, error, status }
29
+ */
30
+ export const useQuickBollinger = (closes: number[]): UseQuickBollingerResult => {
31
+ const { upper, middle, lower, loading, status, error } = useBollinger(closes, {
32
+ period: 20,
33
+ k: 2.0
34
+ });
35
+
36
+ const validUpper = upper.filter(v => v !== null && Number.isFinite(v)) as number[];
37
+ const validMiddle = middle.filter(v => v !== null && Number.isFinite(v)) as number[];
38
+ const validLower = lower.filter(v => v !== null && Number.isFinite(v)) as number[];
39
+
40
+ const u = validUpper.length > 0 ? validUpper[validUpper.length - 1] : null;
41
+ const m = validMiddle.length > 0 ? validMiddle[validMiddle.length - 1] : null;
42
+ const l = validLower.length > 0 ? validLower[validLower.length - 1] : null;
43
+ const price = closes.length > 0 ? closes[closes.length - 1] : null;
44
+
45
+ const currentBands = {
46
+ upper: u,
47
+ middle: m,
48
+ lower: l,
49
+ price,
50
+ position: ((): 'above' | 'within' | 'below' => {
51
+ if (u === null || l === null || price === null) return 'within';
52
+ return price > u ? 'above' : price < l ? 'below' : 'within';
53
+ })()
54
+ };
55
+
56
+ return { upper, middle, lower, loading, error, currentBands, status };
57
+ };
58
+
@@ -0,0 +1,65 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/useQuickDMI.ts
3
+ // Tier 1 API: DMI (Directional Movement Index)
4
+ // ============================================================================
5
+
6
+ import { useDMI } from '../../hooks/useIndicators';
7
+ import type { CandleData } from '../../types';
8
+
9
+ export interface UseQuickDMIResult {
10
+ pdi: (number | null)[];
11
+ mdi: (number | null)[];
12
+ adx: (number | null)[];
13
+ adxr: (number | null)[];
14
+ loading: boolean;
15
+ error?: string;
16
+ currentTrend: {
17
+ pdi: number | null;
18
+ mdi: number | null;
19
+ adx: number | null;
20
+ direction: 'uptrend' | 'downtrend' | 'no_trend';
21
+ strength: 'strong' | 'moderate' | 'weak';
22
+ };
23
+ status: 'success' | 'error';
24
+ }
25
+
26
+ /**
27
+ * Quick DMI Hook - Standard DMI(14)
28
+ *
29
+ * ADX > 25: Strong trend
30
+ * ADX 20-25: Moderate trend
31
+ * ADX < 20: Weak trend / No trend
32
+ *
33
+ * @param candleData - Array of candle data
34
+ * @returns { pdi, mdi, adx, adxr, currentTrend, loading, error, status }
35
+ */
36
+ export const useQuickDMI = (candleData: CandleData[]): UseQuickDMIResult => {
37
+ const { pdi, mdi, adx, adxr, loading, status, error } = useDMI(candleData, {
38
+ period: 14
39
+ });
40
+
41
+ const validPDI = pdi.filter(v => v !== null && Number.isFinite(v)) as number[];
42
+ const validMDI = mdi.filter(v => v !== null && Number.isFinite(v)) as number[];
43
+ const validADX = adx.filter(v => v !== null && Number.isFinite(v)) as number[];
44
+
45
+ const pdiVal = validPDI.length > 0 ? validPDI[validPDI.length - 1] : null;
46
+ const mdiVal = validMDI.length > 0 ? validMDI[validMDI.length - 1] : null;
47
+ const adxVal = validADX.length > 0 ? validADX[validADX.length - 1] : null;
48
+
49
+ const currentTrend = {
50
+ pdi: pdiVal,
51
+ mdi: mdiVal,
52
+ adx: adxVal,
53
+ direction: ((): 'uptrend' | 'downtrend' | 'no_trend' => {
54
+ if (pdiVal === null || mdiVal === null) return 'no_trend';
55
+ return pdiVal > mdiVal ? 'uptrend' : mdiVal > pdiVal ? 'downtrend' : 'no_trend';
56
+ })(),
57
+ strength: ((): 'strong' | 'moderate' | 'weak' => {
58
+ if (adxVal === null) return 'weak';
59
+ return adxVal > 25 ? 'strong' : adxVal > 20 ? 'moderate' : 'weak';
60
+ })()
61
+ };
62
+
63
+ return { pdi, mdi, adx, adxr, loading, error, currentTrend, status };
64
+ };
65
+
@@ -0,0 +1,48 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/useQuickEMA.ts
3
+ // Tier 1 API: Exponential Moving Average
4
+ // ============================================================================
5
+
6
+ import { useEMA } from '../../hooks/useIndicators';
7
+
8
+ export interface UseQuickEMAResult {
9
+ data: (number | null)[];
10
+ loading: boolean;
11
+ error?: string;
12
+ lastValues: {
13
+ current: number | null;
14
+ previous: number | null;
15
+ trend: 'up' | 'down' | 'flat';
16
+ };
17
+ status: 'success' | 'error';
18
+ }
19
+
20
+ /**
21
+ * Quick EMA Hook - No configuration needed
22
+ *
23
+ * Default: EMA12 (fast EMA, commonly used with EMA26)
24
+ *
25
+ * @param closes - Array of close prices
26
+ * @returns { data, loading, error, lastValues, status }
27
+ */
28
+ export const useQuickEMA = (closes: number[]): UseQuickEMAResult => {
29
+ const { data, loading, status, error } = useEMA(closes, {
30
+ period: 12,
31
+ type: 'exponential'
32
+ });
33
+
34
+ const validData = data.filter(v => v !== null && Number.isFinite(v)) as number[];
35
+ const lastValues = {
36
+ current: validData.length > 0 ? validData[validData.length - 1] : null,
37
+ previous: validData.length > 1 ? validData[validData.length - 2] : null,
38
+ trend: ((): 'up' | 'down' | 'flat' => {
39
+ if (validData.length < 2) return 'flat';
40
+ const curr = validData[validData.length - 1];
41
+ const prev = validData[validData.length - 2];
42
+ return curr > prev ? 'up' : curr < prev ? 'down' : 'flat';
43
+ })()
44
+ };
45
+
46
+ return { data, loading, error, lastValues, status };
47
+ };
48
+
@@ -0,0 +1,55 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/useQuickKDJ.ts
3
+ // Tier 1 API: KDJ Stochastic
4
+ // ============================================================================
5
+
6
+ import { useKDJ } from '../../hooks/useIndicators';
7
+ import type { CandleData } from '../../types';
8
+
9
+ export interface UseQuickKDJResult {
10
+ k: (number | null)[];
11
+ d: (number | null)[];
12
+ j: (number | null)[];
13
+ loading: boolean;
14
+ error?: string;
15
+ currentValues: {
16
+ k: number | null;
17
+ d: number | null;
18
+ j: number | null;
19
+ signal: 'buy' | 'sell' | 'neutral';
20
+ };
21
+ status: 'success' | 'error';
22
+ }
23
+
24
+ /**
25
+ * Quick KDJ Hook - Standard KDJ(9,3,3)
26
+ *
27
+ * @param candleData - Array of candle data with OHLC
28
+ * @returns { k, d, j, currentValues, loading, error, status }
29
+ */
30
+ export const useQuickKDJ = (candleData: CandleData[]): UseQuickKDJResult => {
31
+ const { k, d, j, loading, status, error } = useKDJ(candleData, {
32
+ n: 9,
33
+ m1: 3,
34
+ m2: 3
35
+ });
36
+
37
+ const validK = k.filter(v => v !== null && Number.isFinite(v)) as number[];
38
+ const validD = d.filter(v => v !== null && Number.isFinite(v)) as number[];
39
+ const validJ = j.filter(v => v !== null && Number.isFinite(v)) as number[];
40
+
41
+ const currentValues = {
42
+ k: validK.length > 0 ? validK[validK.length - 1] : null,
43
+ d: validD.length > 0 ? validD[validD.length - 1] : null,
44
+ j: validJ.length > 0 ? validJ[validJ.length - 1] : null,
45
+ signal: ((): 'buy' | 'sell' | 'neutral' => {
46
+ if (validK.length === 0 || validD.length === 0) return 'neutral';
47
+ const kVal = validK[validK.length - 1];
48
+ const dVal = validD[validD.length - 1];
49
+ return kVal > dVal && kVal < 80 ? 'buy' : kVal < dVal && kVal > 20 ? 'sell' : 'neutral';
50
+ })()
51
+ };
52
+
53
+ return { k, d, j, loading, error, currentValues, status };
54
+ };
55
+
@@ -0,0 +1,60 @@
1
+ // ============================================================================
2
+ // 📄 bindings/react-native/src/api/tier1/useQuickMA.ts
3
+ // Tier 1 API: Simple Moving Average
4
+ // Usage: const { data, loading, error } = useQuickMA(closes);
5
+ // ============================================================================
6
+
7
+ import { useMA } from '../../hooks/useIndicators';
8
+
9
+ export interface UseQuickMAResult {
10
+ data: (number | null)[];
11
+ loading: boolean;
12
+ error?: string;
13
+ lastValues: {
14
+ current: number | null;
15
+ previous: number | null;
16
+ trend: 'up' | 'down' | 'flat';
17
+ };
18
+ status: 'success' | 'error';
19
+ }
20
+
21
+ /**
22
+ * Quick Moving Average Hook - No configuration needed
23
+ *
24
+ * Default: MA20 (most common period for day traders)
25
+ *
26
+ * @param closes - Array of close prices
27
+ * @returns { data, loading, error, lastValues, status }
28
+ *
29
+ * @example
30
+ * const { data, lastValues } = useQuickMA(closePrices);
31
+ * console.log(`MA20: ${lastValues.current}, Trend: ${lastValues.trend}`);
32
+ */
33
+ export const useQuickMA = (closes: number[]): UseQuickMAResult => {
34
+ const { data, loading, status, error } = useMA(closes, {
35
+ period: 20,
36
+ type: 'simple'
37
+ });
38
+
39
+ // Calculate trend
40
+ const validData = data.filter(v => v !== null && Number.isFinite(v)) as number[];
41
+ const lastValues = {
42
+ current: validData.length > 0 ? validData[validData.length - 1] : null,
43
+ previous: validData.length > 1 ? validData[validData.length - 2] : null,
44
+ trend: ((): 'up' | 'down' | 'flat' => {
45
+ if (validData.length < 2) return 'flat';
46
+ const curr = validData[validData.length - 1];
47
+ const prev = validData[validData.length - 2];
48
+ return curr > prev ? 'up' : curr < prev ? 'down' : 'flat';
49
+ })()
50
+ };
51
+
52
+ return {
53
+ data,
54
+ loading,
55
+ error,
56
+ lastValues,
57
+ status
58
+ };
59
+ };
60
+