rework market trends
This commit is contained in:
+24
-33
@@ -1,12 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import {LoadingSpinner, Tooltip} from '@/components';
|
||||
import {formatIsk} from '@/formaters';
|
||||
import {getHistory, getHistoryQuartils} from '@/market';
|
||||
import {ArrowTrendingDownIcon, ArrowTrendingUpIcon} from '@heroicons/vue/24/outline';
|
||||
import {getQuartiles, type HistoryQuartiles, MarketTrendIcon} from '@/market';
|
||||
import {computedAsync} from '@vueuse/core';
|
||||
import {ref, watchEffect} from 'vue';
|
||||
|
||||
const trendingScale = 3;
|
||||
import {computed, ref} from 'vue';
|
||||
|
||||
interface Props {
|
||||
id: number;
|
||||
@@ -17,42 +14,36 @@ interface Props {
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const open = ref(false);
|
||||
const loading = ref(false);
|
||||
|
||||
const q1 = ref(0);
|
||||
const median = ref(0);
|
||||
const q3 = ref(0);
|
||||
const lineColor = ref('');
|
||||
const history = computedAsync(() => getHistory(props.id), []);
|
||||
const quartiles = computedAsync<HistoryQuartiles | null>(
|
||||
() => open.value && props.id ? getQuartiles(props.id) : null,
|
||||
null,
|
||||
loading,
|
||||
);
|
||||
|
||||
watchEffect(async () => {
|
||||
if (!open.value || !props.id) {
|
||||
return;
|
||||
const lineColor = computed(() => {
|
||||
if (!quartiles.value) {
|
||||
return '';
|
||||
}
|
||||
const quartils = getHistoryQuartils(history.value);
|
||||
|
||||
q1.value = quartils.q1;
|
||||
median.value = quartils.median;
|
||||
q3.value = quartils.q3;
|
||||
|
||||
if (props.buy >= quartils.q3) {
|
||||
lineColor.value = 'line-blue';
|
||||
} else if (props.sell >= quartils.q3) {
|
||||
lineColor.value = 'line-green';
|
||||
} else {
|
||||
lineColor.value = '';
|
||||
if (props.buy >= quartiles.value.q3) {
|
||||
return 'line-blue';
|
||||
}
|
||||
})
|
||||
if (props.sell >= quartiles.value.q3) {
|
||||
return 'line-green';
|
||||
}
|
||||
return '';
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Tooltip v-model:open="open" class="tooltip">
|
||||
<template #header>
|
||||
<LoadingSpinner v-if="history.length < trendingScale" />
|
||||
<ArrowTrendingUpIcon v-else-if="history[0].average > history[trendingScale - 1].average" />
|
||||
<ArrowTrendingDownIcon v-else />
|
||||
<LoadingSpinner v-if="loading || !quartiles" />
|
||||
<MarketTrendIcon v-else :trend="quartiles.trend" />
|
||||
</template>
|
||||
<template #default>
|
||||
<div class="bg-slate-500 -left-1/2 relative tooltip-content" v-if="history.length > 0">
|
||||
<div class="bg-slate-500 -left-1/2 relative tooltip-content" v-if="quartiles">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -63,9 +54,9 @@ watchEffect(async () => {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr :class="lineColor">
|
||||
<td class="text-right text-nowrap">{{ formatIsk(q1) }}</td>
|
||||
<td class="text-right text-nowrap">{{ formatIsk(median) }}</td>
|
||||
<td class="text-right text-nowrap">{{ formatIsk(q3) }}</td>
|
||||
<td class="text-right text-nowrap">{{ formatIsk(quartiles.q1) }}</td>
|
||||
<td class="text-right text-nowrap">{{ formatIsk(quartiles.median) }}</td>
|
||||
<td class="text-right text-nowrap">{{ formatIsk(quartiles.q3) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -5,7 +5,7 @@ import {MinusIcon, PlusIcon} from '@heroicons/vue/24/outline';
|
||||
import {useStorage} from '@vueuse/core';
|
||||
import {computed, ref} from 'vue';
|
||||
import {AcquiredType} from './AcquiredType';
|
||||
import AcquisitionQuantilsTooltip from './AcquisitionQuantilsTooltip.vue';
|
||||
import AcquisitionQuartilesTooltip from './AcquisitionQuartilesTooltip.vue';
|
||||
import {formatEveDate, formatIsk, percentFormater} from "@/formaters.ts";
|
||||
|
||||
type Result = {
|
||||
@@ -189,7 +189,7 @@ const total = computed(() => {
|
||||
<td v-if="showColumn('name')">
|
||||
<div class="flex">
|
||||
<MarketTypeLabel :id="r.data.type.id" :name="r.data.name" />
|
||||
<AcquisitionQuantilsTooltip :id="r.data.type.id" :buy="r.data.buy" :sell="r.data.sell" />
|
||||
<AcquisitionQuartilesTooltip :id="r.data.type.id" :buy="r.data.buy" :sell="r.data.sell" />
|
||||
</div>
|
||||
</td>
|
||||
<td v-if="showColumn('buy')" class="text-right">{{ formatIsk(r.data.buy) }}</td>
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { MarketHistory } from "@/market";
|
||||
|
||||
export type HistoryQuartils = {
|
||||
totalVolume: number,
|
||||
q1: number,
|
||||
median: number,
|
||||
q3: number,
|
||||
}
|
||||
|
||||
export const getHistoryQuartils = (history: MarketHistory[], days?: number): HistoryQuartils => {
|
||||
const now = Date.now();
|
||||
|
||||
const volumes = history
|
||||
.flatMap(h => {
|
||||
const volume = h.volume;
|
||||
|
||||
if (volume === 0 || (days && new Date(h.date).getTime() < now - days * 24 * 60 * 60 * 1000)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const e = estimateVolume(h);
|
||||
|
||||
return [[h.highest, e], [h.lowest, volume - e]];
|
||||
})
|
||||
.filter(h => h[1] > 0)
|
||||
.sort((a, b) => a[0] - b[0]);
|
||||
|
||||
const totalVolume = volumes.reduce((acc, [_, v]) => acc + v, 0);
|
||||
const quartilVolume = totalVolume / 4;
|
||||
const quartils: [number, number, number] = [0, 0, 0];
|
||||
|
||||
let currentVolume = 0;
|
||||
let quartil = 0;
|
||||
|
||||
for (const [price, volume] of volumes) {
|
||||
currentVolume += volume;
|
||||
|
||||
if (currentVolume >= quartilVolume * (quartil + 1)) {
|
||||
quartils[quartil] = price;
|
||||
if (quartil === 2) {
|
||||
break;
|
||||
}
|
||||
quartil++;
|
||||
}
|
||||
}
|
||||
return {
|
||||
totalVolume,
|
||||
q1: quartils[0],
|
||||
median: quartils[1],
|
||||
q3: quartils[2],
|
||||
};
|
||||
}
|
||||
|
||||
const estimateVolume = (history: MarketHistory): number => {
|
||||
if (history.volume === 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(1, Math.round(history.volume * ((history.average - history.lowest) / (history.highest - history.lowest))));
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { MarketHistoryResponse } from "@/generated/mammon";
|
||||
import { marketApi } from "@/mammon";
|
||||
|
||||
export type MarketHistory = MarketHistoryResponse;
|
||||
|
||||
export const getHistory = async (typeId: number): Promise<MarketHistory[]> =>
|
||||
(await marketApi.findHistory(typeId)).data;
|
||||
@@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import {type MarketTrend, MarketTrends} from '@/market';
|
||||
import {ArrowLongRightIcon, ArrowTrendingDownIcon, ArrowTrendingUpIcon} from '@heroicons/vue/24/outline';
|
||||
|
||||
interface Props {
|
||||
trend: MarketTrend;
|
||||
}
|
||||
|
||||
defineProps<Props>();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ArrowTrendingUpIcon v-if="trend === MarketTrends.Up" />
|
||||
<ArrowTrendingDownIcon v-else-if="trend === MarketTrends.Down" />
|
||||
<ArrowLongRightIcon v-else />
|
||||
</template>
|
||||
@@ -0,0 +1,13 @@
|
||||
import {
|
||||
HistoryQuartilesResponse,
|
||||
HistoryQuartilesResponseTrendEnum,
|
||||
MarketScanResponseTrendEnum
|
||||
} from "@/generated/mammon";
|
||||
import {marketApi} from "@/mammon";
|
||||
|
||||
export type HistoryQuartiles = HistoryQuartilesResponse;
|
||||
|
||||
export type MarketTrend = MarketScanResponseTrendEnum | HistoryQuartilesResponseTrendEnum;
|
||||
export const MarketTrends = MarketScanResponseTrendEnum as const;
|
||||
|
||||
export const getQuartiles = async (typeId: number, days?: number): Promise<HistoryQuartiles> => (await marketApi.findQuartiles(typeId, days)).data;
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './MarketHistory';
|
||||
export * from './HistoryQuartils';
|
||||
export * from './history.ts';
|
||||
|
||||
export { default as MarketTrendIcon } from './MarketTrendIcon.vue';
|
||||
|
||||
Reference in New Issue
Block a user