Versie 0.2.0
This commit is contained in:
commit
e77d8fd373
495 files changed
+81026
No files matched your search
@@ -0,0 +1,28 @@
|
||||
// Generated, do not edit. sha256:9ca0c45b0945e94868e99e2fd93c72e9ebb7d1e8b99a59b57aa8e5980d47892d
|
||||
export const GENESIS_ISO = '2009-01-03';
|
||||
|
||||
export const POWERLAW_PROJECT_DAYS = 365;
|
||||
|
||||
export const POWERLAW = Object.freeze({
|
||||
FIT: Object.freeze({ A: 1e-17, N: 5.8 }),
|
||||
LOWER: Object.freeze({ DEFAULT: 0.47 }),
|
||||
UPPER: Object.freeze({ DEFAULT_BY_CCY: Object.freeze({ EUR: 4.5, USD: 5.0 }) }),
|
||||
});
|
||||
|
||||
export const WMA = Object.freeze({
|
||||
WEEKS: 200,
|
||||
FAST_WEEKS: 50,
|
||||
SIGNAL_MULT: 1.2,
|
||||
RED_MULT: 1.5,
|
||||
});
|
||||
|
||||
export const MAYER = Object.freeze({
|
||||
DAYS: 200,
|
||||
GREEN_BELOW: 1.0,
|
||||
RED_ABOVE: 2.4,
|
||||
});
|
||||
|
||||
export const ZOOM_RECENT_DAYS = 180;
|
||||
|
||||
export const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
||||
export const MS_PER_WEEK = 7 * MS_PER_DAY;
|
||||
@@ -0,0 +1,127 @@
|
||||
// Generated, do not edit. sha256:19640cdaa8812f06608f0f5d7ab246cafacd608773aca0ad6879af4404a7d7ed
|
||||
import { GENESIS_ISO, MS_PER_WEEK, MS_PER_DAY, WMA, MAYER, POWERLAW } from './constants.js';
|
||||
import { t } from '../i18n/index.js';
|
||||
|
||||
const GENESIS_MS = new Date(GENESIS_ISO).getTime();
|
||||
|
||||
export function daysSinceGenesis(tsMs) {
|
||||
return (tsMs - GENESIS_MS) / (24 * 60 * 60 * 1000);
|
||||
}
|
||||
export function genesisMs() { return GENESIS_MS; }
|
||||
|
||||
export function toDailyGrid(rows) {
|
||||
const byDay = new Map();
|
||||
for (const r of rows) {
|
||||
const v = r.fiatPrice;
|
||||
if (v != null && v > 0) byDay.set(r.observedAt.slice(0, 10), v);
|
||||
}
|
||||
if (byDay.size === 0) throw new Error(t('signals.noHistory'));
|
||||
const days = [...byDay.keys()].sort();
|
||||
const startMs = Date.parse(`${days[0]}T00:00:00Z`);
|
||||
const endMs = Date.parse(`${days[days.length - 1]}T00:00:00Z`);
|
||||
const out = [];
|
||||
let last = null;
|
||||
for (let ms = startMs; ms <= endMs; ms += MS_PER_DAY) {
|
||||
const iso = new Date(ms).toISOString().slice(0, 10);
|
||||
if (byDay.has(iso)) last = byDay.get(iso);
|
||||
out.push([ms, last]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function dailyGridSources(rows) {
|
||||
const byDay = new Map();
|
||||
for (const r of rows) {
|
||||
const v = r.fiatPrice;
|
||||
if (v != null && v > 0) {
|
||||
byDay.set(Date.parse(`${r.observedAt.slice(0, 10)}T00:00:00Z`), { providerId: r.providerId, sourceName: r.sourceName ?? null });
|
||||
}
|
||||
}
|
||||
return byDay;
|
||||
}
|
||||
|
||||
export function powerLawModel() {
|
||||
return { A: POWERLAW.FIT.A, n: POWERLAW.FIT.N };
|
||||
}
|
||||
export function powerLawValue(fit, tsMs) {
|
||||
return fit.A * Math.pow(daysSinceGenesis(tsMs), fit.n) * (fit.scaleAt ? fit.scaleAt(tsMs) : 1);
|
||||
}
|
||||
|
||||
export function powerLawFor(currency, usdRates = null) {
|
||||
const own = POWERLAW.UPPER.DEFAULT_BY_CCY[String(currency).toUpperCase()];
|
||||
if (own != null) return { fit: powerLawModel(), upper: own };
|
||||
if (!usdRates || !usdRates.length) return null;
|
||||
return { fit: { ...powerLawModel(), scaleAt: ts => rateOn(usdRates, ts) }, upper: POWERLAW.UPPER.DEFAULT_BY_CCY.USD };
|
||||
}
|
||||
|
||||
function rateOn(grid, ts) {
|
||||
let lo = 0, hi = grid.length - 1, hit = 0;
|
||||
while (lo <= hi) {
|
||||
const mid = (lo + hi) >> 1;
|
||||
if (grid[mid][0] <= ts) { hit = mid; lo = mid + 1; } else hi = mid - 1;
|
||||
}
|
||||
return grid[hit][1];
|
||||
}
|
||||
|
||||
export function weeklyAggregate(daily) {
|
||||
const out = [];
|
||||
if (daily.length === 0) return out;
|
||||
let week = Math.floor(daily[0][0] / MS_PER_WEEK);
|
||||
let sum = 0, count = 0;
|
||||
for (const [ts, price] of daily) {
|
||||
const w = Math.floor(ts / MS_PER_WEEK);
|
||||
if (w !== week) {
|
||||
if (count > 0) out.push([week * MS_PER_WEEK, sum / count]);
|
||||
week = w; sum = price; count = 1;
|
||||
} else { sum += price; count++; }
|
||||
}
|
||||
if (count > 0) out.push([week * MS_PER_WEEK, sum / count]);
|
||||
return out;
|
||||
}
|
||||
|
||||
export function movingAverage(series, window) {
|
||||
const out = [];
|
||||
let sum = 0;
|
||||
for (let i = 0; i < series.length; i++) {
|
||||
sum += series[i][1];
|
||||
if (i >= window) sum -= series[i - window][1];
|
||||
out.push({ x: series[i][0], y: i >= window - 1 ? sum / window : null });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
export function mayerSeries(daily, days) {
|
||||
const ma = movingAverage(daily, days);
|
||||
return daily.map(([ts, price], i) => ({
|
||||
x: ts,
|
||||
y: ma[i].y == null ? null : price / ma[i].y,
|
||||
}));
|
||||
}
|
||||
|
||||
export function powerLawSignal(daily, fit, lowerFactor, upperFactor) {
|
||||
const [ts, price] = daily[daily.length - 1];
|
||||
const f = powerLawValue(fit, ts);
|
||||
if (price <= f * lowerFactor) return { color: 'green', key: 'plStrong' };
|
||||
if (price <= f) return { color: 'green', key: 'plBuy' };
|
||||
if (price <= f * upperFactor) return { color: 'yellow', key: 'plAbove' };
|
||||
return { color: 'red', key: 'plFar' };
|
||||
}
|
||||
|
||||
export function wmaSignal(weekly) {
|
||||
const ma = movingAverage(weekly, WMA.WEEKS);
|
||||
const lastWma = [...ma].reverse().find(d => d.y != null);
|
||||
if (!lastWma || weekly.length === 0) return null;
|
||||
const ratio = weekly[weekly.length - 1][1] / lastWma.y;
|
||||
if (ratio < WMA.SIGNAL_MULT) return { color: 'green', key: 'wmaBuy', mult: ratio };
|
||||
if (ratio < WMA.RED_MULT) return { color: 'yellow', key: 'wmaAbove', mult: ratio };
|
||||
return { color: 'red', key: 'wmaFar', mult: ratio };
|
||||
}
|
||||
|
||||
export function mayerSignal(mayer) {
|
||||
const last = [...mayer].reverse().find(d => d.y != null);
|
||||
if (!last) return null;
|
||||
const v = last.y;
|
||||
if (v < MAYER.GREEN_BELOW) return { color: 'green', key: 'mayerLow', val: v };
|
||||
if (v < MAYER.RED_ABOVE) return { color: 'yellow', key: 'mayerMid', val: v };
|
||||
return { color: 'red', key: 'mayerHigh', val: v };
|
||||
}
|
||||
Reference in new issue
Block a user