|
@@ -10,6 +10,7 @@ async function getROI(cx: string) {
|
|
|
|
|
|
|
|
const lowVolume = document.querySelector('input#low-volume') as HTMLInputElement;
|
|
const lowVolume = document.querySelector('input#low-volume') as HTMLInputElement;
|
|
|
|
|
|
|
|
|
|
+const cxSelect = document.querySelector('select#cx') as HTMLSelectElement;
|
|
|
const expertise = {
|
|
const expertise = {
|
|
|
AGRICULTURE: 'agri',
|
|
AGRICULTURE: 'agri',
|
|
|
CHEMISTRY: 'chem',
|
|
CHEMISTRY: 'chem',
|
|
@@ -28,7 +29,7 @@ for (const key of Object.keys(expertise)) {
|
|
|
option.textContent = key.replace('_', ' ').toLowerCase();
|
|
option.textContent = key.replace('_', ' ').toLowerCase();
|
|
|
expertiseSelect.appendChild(option);
|
|
expertiseSelect.appendChild(option);
|
|
|
}
|
|
}
|
|
|
-const cxSelect = document.querySelector('select#cx') as HTMLSelectElement;
|
|
|
|
|
|
|
+const buildingSelect = document.querySelector('select#building') as HTMLSelectElement;
|
|
|
|
|
|
|
|
const formatDecimal = new Intl.NumberFormat(undefined,
|
|
const formatDecimal = new Intl.NumberFormat(undefined,
|
|
|
{maximumFractionDigits: 2, maximumSignificantDigits: 6, roundingPriority: 'lessPrecision'}).format;
|
|
{maximumFractionDigits: 2, maximumSignificantDigits: 6, roundingPriority: 'lessPrecision'}).format;
|
|
@@ -43,12 +44,35 @@ async function render() {
|
|
|
roiCache[cx] = getROI(cx);
|
|
roiCache[cx] = getROI(cx);
|
|
|
const {lastModified, profits} = await roiCache[cx];
|
|
const {lastModified, profits} = await roiCache[cx];
|
|
|
|
|
|
|
|
|
|
+ const buildingTickers = new Set(profits.map(p => p.building));
|
|
|
|
|
+ const buildings: {ticker: string, expertise: keyof typeof expertise}[] = Array.from(buildingTickers)
|
|
|
|
|
+ .map((building) => ({ticker: building, expertise: profits.find(p => p.building === building)!.expertise}))
|
|
|
|
|
+ .sort((a, b) => a.ticker.localeCompare(b.ticker));
|
|
|
|
|
+ let selectedBuilding = buildingSelect.value;
|
|
|
|
|
+ let buildingFound = false;
|
|
|
|
|
+ buildingSelect.innerHTML = '<option value="">(all)</option>';
|
|
|
|
|
+ for (const building of buildings)
|
|
|
|
|
+ if (expertiseSelect.value === '' || expertiseSelect.value === building.expertise) {
|
|
|
|
|
+ const option = document.createElement('option');
|
|
|
|
|
+ option.value = building.ticker;
|
|
|
|
|
+ option.textContent = building.ticker;
|
|
|
|
|
+ if (building.ticker === selectedBuilding) {
|
|
|
|
|
+ buildingFound = true;
|
|
|
|
|
+ option.selected = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ buildingSelect.appendChild(option);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (!buildingFound)
|
|
|
|
|
+ selectedBuilding = '';
|
|
|
|
|
+
|
|
|
for (const p of profits) {
|
|
for (const p of profits) {
|
|
|
const volumeRatio = p.output_per_day / p.average_traded_7d;
|
|
const volumeRatio = p.output_per_day / p.average_traded_7d;
|
|
|
if (!lowVolume.checked && volumeRatio > 0.05)
|
|
if (!lowVolume.checked && volumeRatio > 0.05)
|
|
|
continue;
|
|
continue;
|
|
|
if (expertiseSelect.value !== '' && p.expertise !== expertiseSelect.value)
|
|
if (expertiseSelect.value !== '' && p.expertise !== expertiseSelect.value)
|
|
|
continue;
|
|
continue;
|
|
|
|
|
+ if (selectedBuilding !== '' && p.building !== selectedBuilding)
|
|
|
|
|
+ continue;
|
|
|
const tr = document.createElement('tr');
|
|
const tr = document.createElement('tr');
|
|
|
const profitPerArea = p.profit_per_day / p.area;
|
|
const profitPerArea = p.profit_per_day / p.area;
|
|
|
const breakEven = p.profit_per_day > 0 ? p.capex / p.profit_per_day : Infinity;
|
|
const breakEven = p.profit_per_day > 0 ? p.capex / p.profit_per_day : Infinity;
|
|
@@ -98,14 +122,16 @@ function formatMatPrices(matPrices: MatPrice[]): string {
|
|
|
|
|
|
|
|
setupPopover();
|
|
setupPopover();
|
|
|
lowVolume.addEventListener('change', render);
|
|
lowVolume.addEventListener('change', render);
|
|
|
-expertiseSelect.addEventListener('change', render);
|
|
|
|
|
cxSelect.addEventListener('change', render);
|
|
cxSelect.addEventListener('change', render);
|
|
|
|
|
+expertiseSelect.addEventListener('change', render);
|
|
|
|
|
+buildingSelect.addEventListener('change', render);
|
|
|
render();
|
|
render();
|
|
|
|
|
|
|
|
interface Profit {
|
|
interface Profit {
|
|
|
outputs: MatPrice[]
|
|
outputs: MatPrice[]
|
|
|
recipe: string
|
|
recipe: string
|
|
|
expertise: keyof typeof expertise
|
|
expertise: keyof typeof expertise
|
|
|
|
|
+ building: string
|
|
|
profit_per_day: number
|
|
profit_per_day: number
|
|
|
area: number
|
|
area: number
|
|
|
capex: number
|
|
capex: number
|
|
@@ -123,3 +149,9 @@ interface MatPrice {
|
|
|
amount: number
|
|
amount: number
|
|
|
vwap_7d: number
|
|
vwap_7d: number
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+interface Building {
|
|
|
|
|
+ building_type: 'INFRASTRUCTURE' | 'PLANETARY' | 'PRODUCTION';
|
|
|
|
|
+ building_ticker: string;
|
|
|
|
|
+ expertise: keyof typeof expertise;
|
|
|
|
|
+}
|