@exodus/market-history 3.1.2 → 4.0.1
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 +16 -0
- package/README.md +32 -32
- package/atoms/index.js +8 -0
- package/module/index.js +78 -30
- package/package.json +15 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [4.0.1](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/market-history@4.0.0...@exodus/market-history@4.0.1) (2023-07-05)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **market-history:** publish atoms dir ([#2287](https://github.com/ExodusMovement/exodus-hydra/issues/2287)) ([17349b1](https://github.com/ExodusMovement/exodus-hydra/commit/17349b1a1183598d5ee6033c0bbe7e7b9e56ceaa))
|
|
11
|
+
|
|
12
|
+
## [4.0.0](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/market-history@3.1.2...@exodus/market-history@4.0.0) (2023-07-05)
|
|
13
|
+
|
|
14
|
+
### ⚠ BREAKING CHANGES
|
|
15
|
+
|
|
16
|
+
- market-history atom (#2160)
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
- market-history atom ([#2160](https://github.com/ExodusMovement/exodus-hydra/issues/2160)) ([b74364a](https://github.com/ExodusMovement/exodus-hydra/commit/b74364a47e8a51b3eea6b8cc870c731def65726b))
|
|
21
|
+
|
|
6
22
|
## [3.1.2](https://github.com/ExodusMovement/exodus-hydra/compare/@exodus/market-history@3.1.1...@exodus/market-history@3.1.2) (2023-06-21)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @exodus/market-history
|
package/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# @exodus/market-history
|
|
2
2
|
|
|
3
|
-
module fetches daily and hourly historical prices for assets by interval
|
|
4
|
-
|
|
3
|
+
module fetches daily and hourly historical prices for assets by interval.
|
|
4
|
+
Return prices on interval `close`.
|
|
5
|
+
|
|
6
|
+
Public methods:
|
|
7
|
+
`start` - starts monitor
|
|
8
|
+
`update` update prices by granularity (day|hour) manually
|
|
5
9
|
|
|
6
10
|
## Install
|
|
7
11
|
|
|
@@ -16,44 +20,40 @@ import createMarketHistoryMonitor from '@exodus/market-history/module'
|
|
|
16
20
|
|
|
17
21
|
const marketHistoryMonitor = createMarketHistoryMonitor.factory({
|
|
18
22
|
assetsModule,
|
|
19
|
-
currencyAtom,
|
|
20
23
|
storage: marketHistoryStorage,
|
|
24
|
+
currencyAtom,
|
|
21
25
|
pricingClient,
|
|
22
26
|
getCacheKey: ({ currency, assetName, granularity }) =>
|
|
23
27
|
`prices-${currency}-${assetName}-${granularity}`, // optional
|
|
24
|
-
remoteConfigRefreshIntervalAtom, // optional
|
|
28
|
+
remoteConfigRefreshIntervalAtom, // optional. stores interval in ms. if defined we compare distance in ms from previous fetch and if it's larger than defined in this atom we ignore cache.
|
|
25
29
|
clearCacheAtom, // optional. set value if you want to clear cache.
|
|
26
30
|
remoteConfigClearCacheAtom, // optional. If you want to clear cache set any value in this atom.
|
|
31
|
+
enabledAssetsAtom, // we fetch prices only for enabled assets
|
|
32
|
+
marketHistoryAtom, // data stored here
|
|
27
33
|
})
|
|
28
34
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// 1663671600000: 120000,
|
|
52
|
-
// 1663675200000: 100000000,
|
|
53
|
-
// }
|
|
54
|
-
// }
|
|
55
|
-
})
|
|
35
|
+
// stores market history in marketHistoryAtom in following format
|
|
36
|
+
// marketHistoryAtom.get()
|
|
37
|
+
// {
|
|
38
|
+
// data: {
|
|
39
|
+
// USD: {
|
|
40
|
+
// daily: {
|
|
41
|
+
// bitcoin: {
|
|
42
|
+
// 1663668000000: 90000,
|
|
43
|
+
// }
|
|
44
|
+
// }
|
|
45
|
+
// }
|
|
46
|
+
// },
|
|
47
|
+
// changes: {
|
|
48
|
+
// USD: {
|
|
49
|
+
// daily: {
|
|
50
|
+
// bitcoin: {
|
|
51
|
+
// 1663671600000: 9000,
|
|
52
|
+
// }
|
|
53
|
+
// }
|
|
54
|
+
// }
|
|
55
|
+
// }
|
|
56
|
+
// }
|
|
56
57
|
|
|
57
58
|
marketHistoryMonitor.start()
|
|
58
|
-
marketHistoryMonitor.sync() // force to emit prices
|
|
59
59
|
```
|
package/atoms/index.js
ADDED
package/module/index.js
CHANGED
|
@@ -19,6 +19,19 @@ const transformPriceEntries = (entries = []) => {
|
|
|
19
19
|
const transformPricesByAssetName = (pricesByAssetName) =>
|
|
20
20
|
mapValues(pricesByAssetName, (entries) => transformPriceEntries(entries))
|
|
21
21
|
|
|
22
|
+
const mergePricesInAtom = ({ prices, atomData, parsedGranularity, currency }) => {
|
|
23
|
+
return {
|
|
24
|
+
...atomData,
|
|
25
|
+
[currency]: {
|
|
26
|
+
...atomData[currency],
|
|
27
|
+
[parsedGranularity]: {
|
|
28
|
+
...atomData[currency]?.[parsedGranularity],
|
|
29
|
+
...prices,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
22
35
|
// when provided cache version from local or remote config is different from stored on device we clear the cache and fetch new prices
|
|
23
36
|
const _invalidateStorageCache = async ({
|
|
24
37
|
storage,
|
|
@@ -64,7 +77,9 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
64
77
|
#clearCacheAtom = null
|
|
65
78
|
#remoteConfigClearCacheAtom = null
|
|
66
79
|
#runtimeCache = new Map()
|
|
80
|
+
#marketHistoryAtom = null
|
|
67
81
|
#enabledAssetsAtom = null
|
|
82
|
+
#logger = null
|
|
68
83
|
|
|
69
84
|
constructor({
|
|
70
85
|
assetsModule,
|
|
@@ -76,6 +91,8 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
76
91
|
clearCacheAtom = createInMemoryAtom({ defaultValue: null }),
|
|
77
92
|
remoteConfigClearCacheAtom = createInMemoryAtom({ defaultValue: null }),
|
|
78
93
|
enabledAssetsAtom,
|
|
94
|
+
marketHistoryAtom,
|
|
95
|
+
logger,
|
|
79
96
|
}) {
|
|
80
97
|
super({ name: 'MarketHistoryMonitor' })
|
|
81
98
|
|
|
@@ -88,6 +105,8 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
88
105
|
this.#remoteConfigClearCacheAtom = remoteConfigClearCacheAtom
|
|
89
106
|
this.#currencyAtom = currencyAtom
|
|
90
107
|
this.#enabledAssetsAtom = enabledAssetsAtom
|
|
108
|
+
this.#marketHistoryAtom = marketHistoryAtom
|
|
109
|
+
this.#logger = logger
|
|
91
110
|
}
|
|
92
111
|
|
|
93
112
|
#started = false
|
|
@@ -201,9 +220,17 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
201
220
|
granularity,
|
|
202
221
|
assetNames,
|
|
203
222
|
})
|
|
204
|
-
|
|
223
|
+
|
|
224
|
+
await this.#marketHistoryAtom.set(async (current) => ({
|
|
225
|
+
data: mergePricesInAtom({
|
|
226
|
+
prices,
|
|
227
|
+
atomData: current?.data ?? {},
|
|
228
|
+
currency,
|
|
229
|
+
parsedGranularity,
|
|
230
|
+
}),
|
|
231
|
+
}))
|
|
205
232
|
} catch (error) {
|
|
206
|
-
|
|
233
|
+
this.#logger.error(
|
|
207
234
|
'MarketHistoryMonitor: Failed to fetch rates',
|
|
208
235
|
assetNames,
|
|
209
236
|
currency,
|
|
@@ -222,14 +249,13 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
222
249
|
const currency = this.#currency
|
|
223
250
|
|
|
224
251
|
try {
|
|
225
|
-
|
|
252
|
+
return await this.#fetch({
|
|
226
253
|
currency,
|
|
227
254
|
granularity,
|
|
228
255
|
assetNames,
|
|
229
256
|
})
|
|
230
|
-
this.emit('market-history-new-assets', { currency, granularity: parsedGranularity, prices })
|
|
231
257
|
} catch (error) {
|
|
232
|
-
|
|
258
|
+
this.#logger.error(
|
|
233
259
|
'MarketHistoryMonitor: Failed to fetch rates for assets',
|
|
234
260
|
assetNames,
|
|
235
261
|
currency,
|
|
@@ -238,34 +264,56 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
238
264
|
)
|
|
239
265
|
}
|
|
240
266
|
}
|
|
241
|
-
#updateNewAssets = (assetNames) => {
|
|
242
|
-
return Promise.all([
|
|
243
|
-
this.#updateAssets('hour', assetNames),
|
|
244
|
-
this.#updateAssets('day', assetNames),
|
|
245
|
-
])
|
|
246
|
-
}
|
|
247
|
-
#syncGranularity = async (granularity) => {
|
|
248
|
-
const parsedGranularity = parseGranularity(granularity)
|
|
267
|
+
#updateNewAssets = async (assetNames) => {
|
|
249
268
|
const currency = this.#currency
|
|
250
|
-
const enabledAssets = await this.#enabledAssetsAtom.get()
|
|
251
269
|
|
|
252
|
-
const
|
|
253
|
-
Object.keys(enabledAssets).map((assetName) => {
|
|
254
|
-
const key = this.#getCacheKey({ currency, assetName, granularity })
|
|
255
|
-
return [assetName, this.#runtimeCache.get(key)]
|
|
256
|
-
})
|
|
257
|
-
)
|
|
270
|
+
const granularities = ['hour', 'day']
|
|
258
271
|
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
272
|
+
const promises = granularities.map((granularity) => this.#updateAssets(granularity, assetNames))
|
|
273
|
+
|
|
274
|
+
const results = await Promise.all(promises)
|
|
275
|
+
let changes = {}
|
|
276
|
+
const updateChanges = ({ prices, granularity }) => {
|
|
277
|
+
changes = {
|
|
278
|
+
...changes,
|
|
279
|
+
[currency]: {
|
|
280
|
+
...changes[currency],
|
|
281
|
+
[granularity]: prices,
|
|
282
|
+
},
|
|
283
|
+
}
|
|
284
|
+
}
|
|
262
285
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
286
|
+
granularities.forEach((granularity, index) => {
|
|
287
|
+
const result = results[index]
|
|
288
|
+
if (result) {
|
|
289
|
+
updateChanges({ prices: result, granularity: parseGranularity(granularity) })
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
if (Object.keys(changes).length > 0) {
|
|
294
|
+
await this.#marketHistoryAtom.set((current) => {
|
|
295
|
+
let data = current?.data ?? {}
|
|
296
|
+
granularities.forEach((granularity, index) => {
|
|
297
|
+
const result = results[index]
|
|
298
|
+
if (result) {
|
|
299
|
+
data = mergePricesInAtom({
|
|
300
|
+
prices: result,
|
|
301
|
+
atomData: data,
|
|
302
|
+
currency,
|
|
303
|
+
parsedGranularity: parseGranularity(granularity),
|
|
304
|
+
})
|
|
305
|
+
}
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
data,
|
|
310
|
+
changes,
|
|
311
|
+
}
|
|
312
|
+
})
|
|
313
|
+
}
|
|
266
314
|
}
|
|
267
315
|
|
|
268
|
-
invalidateStorage = async ({ remoteConfigClearCacheVersion }) => {
|
|
316
|
+
#invalidateStorage = async ({ remoteConfigClearCacheVersion }) => {
|
|
269
317
|
const clearCacheVersion = await this.#clearCacheAtom.get() // don't think we need to observe it considering this runs only on startup and set manually in config
|
|
270
318
|
return _invalidateStorageCache({
|
|
271
319
|
storage: this.storage,
|
|
@@ -280,7 +328,7 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
280
328
|
#listenRemoteConfigClearCacheVersionChanges = () => {
|
|
281
329
|
difference(this.#remoteConfigClearCacheAtom).observe(async ({ current, previous }) => {
|
|
282
330
|
if (previous !== undefined && current !== previous) {
|
|
283
|
-
await this
|
|
331
|
+
await this.#invalidateStorage({ remoteConfigClearCacheVersion: current })
|
|
284
332
|
await this.#updateAll()
|
|
285
333
|
}
|
|
286
334
|
})
|
|
@@ -309,14 +357,13 @@ class MarketHistoryMonitor extends ExodusModule {
|
|
|
309
357
|
|
|
310
358
|
start = async () => {
|
|
311
359
|
if (this.#started) {
|
|
312
|
-
await this.sync()
|
|
313
360
|
return
|
|
314
361
|
}
|
|
315
362
|
|
|
316
363
|
this.#started = true
|
|
317
364
|
|
|
318
365
|
const remoteConfigClearCacheVersion = await this.#remoteConfigClearCacheAtom.get()
|
|
319
|
-
await this
|
|
366
|
+
await this.#invalidateStorage({ remoteConfigClearCacheVersion })
|
|
320
367
|
this.#listenRemoteConfigClearCacheVersionChanges()
|
|
321
368
|
|
|
322
369
|
const combinedAtom = combine({
|
|
@@ -364,5 +411,6 @@ export default {
|
|
|
364
411
|
'remoteConfigClearCacheAtom',
|
|
365
412
|
'remoteConfigRefreshIntervalAtom',
|
|
366
413
|
'enabledAssetsAtom',
|
|
414
|
+
'marketHistoryAtom',
|
|
367
415
|
],
|
|
368
416
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/market-history",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"author": "Exodus Movement Inc",
|
|
3
|
+
"version": "4.0.1",
|
|
4
|
+
"description": "Fetches historical prices for assets",
|
|
5
|
+
"author": "Exodus Movement Inc.",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"lint": "eslint . --ignore-path ../../.gitignore",
|
|
@@ -11,10 +11,11 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"module",
|
|
14
|
+
"atoms",
|
|
14
15
|
"utils.js",
|
|
15
16
|
"CHANGELOG.md",
|
|
16
17
|
"README.md",
|
|
17
|
-
"!**/__tests__"
|
|
18
|
+
"!**/__tests__/**"
|
|
18
19
|
],
|
|
19
20
|
"dependencies": {
|
|
20
21
|
"@exodus/basic-utils": "^2.0.0",
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
"devDependencies": {
|
|
26
27
|
"@exodus/assets": "^8.0.88",
|
|
27
28
|
"@exodus/assets-base": "^8.1.6",
|
|
28
|
-
"@exodus/atoms": "^5.
|
|
29
|
+
"@exodus/atoms": "^5.2.1",
|
|
29
30
|
"@exodus/storage-memory": "^2.1.0",
|
|
30
31
|
"eslint": "^8.33.0",
|
|
31
32
|
"events": "^3.3.0",
|
|
@@ -34,5 +35,13 @@
|
|
|
34
35
|
"peerDependencies": {
|
|
35
36
|
"debug": ">= 3.0.0"
|
|
36
37
|
},
|
|
37
|
-
"
|
|
38
|
+
"homepage": "https://github.com/ExodusMovement/exodus-hydra/tree/master/modules/crypto-news-monitor",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/ExodusMovement/exodus-hydra/issues?q=is%3Aissue+is%3Aopen+label%3Crypto-news-monitor"
|
|
41
|
+
},
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/ExodusMovement/exodus-hydra.git"
|
|
45
|
+
},
|
|
46
|
+
"gitHead": "07ccaeaebac57e34e8162bae3ecfa2e98c216469"
|
|
38
47
|
}
|