Last active
September 9, 2024 23:22
-
-
Save zobzn/95a427d546624a3215e1d83c8916c98f to your computer and use it in GitHub Desktop.
tampermonkey etoro-watchlist
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name etoro watchlist | |
// @version 2024-09-09 | |
// @author zobzn | |
// @description try to take over the world! | |
// @namespace https://gist.github.com/zobzn/95a427d546624a3215e1d83c8916c98f | |
// @updateURL https://gist.githubusercontent.com/zobzn/95a427d546624a3215e1d83c8916c98f/raw/etoro-watchlist.js | |
// @downloadURL https://gist.githubusercontent.com/zobzn/95a427d546624a3215e1d83c8916c98f/raw/etoro-watchlist.js | |
// @icon https://etoro-cdn.etorostatic.com/web-client/favicon/favicon-32x32.png | |
// @match *://www.etoro.com/watchlists | |
// @match *://www.etoro.com/markets/* | |
// @match *://www.etoro.com/markets/*/chart | |
// @require https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js | |
// @require https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js | |
// @run-at document-end | |
// @grant GM_setValue | |
// @grant GM_getValue | |
// @grant GM_setClipboard | |
// @grant GM_addStyle | |
// @grant unsafeWindow | |
// @grant window.close | |
// @grant window.focus | |
// @grant window.onurlchange | |
// ==/UserScript== | |
(async () => { | |
"use strict"; | |
const { keyBy, pick, omit } = _; | |
const { DateTime } = luxon; | |
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); | |
const f = (...args) => fetch(...args).then((res) => res.json()); | |
const $ = (selector, context = document) => context.querySelector(selector); | |
const $$ = (selector, context = document) => [ | |
...context.querySelectorAll(selector), | |
]; | |
const waitFor = async (selector, callback) => { | |
do { | |
const elements = $$(selector); | |
if (elements.length) { | |
await callback(elements); | |
break; | |
} else { | |
await sleep(500); | |
} | |
} while (true); | |
}; | |
const waitForEach = async (selector, callback) => { | |
await waitFor(selector, async (elements) => { | |
for (const el of elements) { | |
await callback(el); | |
} | |
}); | |
}; | |
GM_addStyle(` | |
.price-range .low {color: red; } | |
`); | |
if (false) { | |
const [d1, d2, d3] = await Promise.all([ | |
f( | |
"https://api.etorostatic.com/sapi/trade-real/instruments/bulk-slim?InstrumentDataFilters=TradingData&bulkNumber=1&totalBulks=1" | |
).then((r) => keyBy(r.Instruments, "InstrumentID")), | |
f( | |
"https://api.etorostatic.com/sapi/instrumentsmetadata/V1.1/instruments/bulk?bulkNumber=1&totalBulks=1" | |
).then((r) => keyBy(r.InstrumentDisplayDatas, "InstrumentID")), | |
f( | |
"https://www.etoro.com/sapi/trade-real/instruments?InstrumentDataFilters=Activity,Rates,ActivityInExchange" | |
).then((r) => keyBy(r.Rates, "InstrumentID")), | |
]); | |
const symbols = Object.keys(d1) | |
.map((k) => ({ ...d1[k], ...d2[k], rates: d3[k] })) | |
.map((it) => ({ ...it, id: it.InstrumentID, code: it.SymbolFull })); | |
const symbolsByID = keyBy(symbols, "id"); | |
const symbolsByCode = keyBy(symbols, "code"); | |
console.log(pick(symbolsByCode, ["NSDQ100", "BTC"])); | |
} | |
const onInit = async ({ url }) => { | |
await waitForEach( | |
"#watchlist-instruments .et-table-row .bar-info", | |
(some) => { | |
const row = some.closest(".et-table-row"); | |
const barInfo = $(".bar-info", row); | |
const barInfoCell = barInfo.closest(".et-table-cell"); | |
// barInfoCell.style.maxWidth = "60px"; | |
barInfo.innerHTML = `<div class="spread"></div> - <div class="from-high"></div>`; | |
} | |
); | |
setInterval(() => { | |
$$( | |
"#watchlist-instruments .et-table-row .price-range .low:not(:empty)" | |
).forEach((some) => { | |
const row = some.closest(".et-table-row"); | |
const range = $(".price-range", row); | |
const min = parseFloat($(".price-range .low", row).innerText); | |
const max = parseFloat($(".price-range .high", row).innerText); | |
const sell = parseFloat( | |
$('[automation-id="buy-sell-button-container-sell"] .price', row) | |
.innerText | |
); | |
const buy = parseFloat( | |
$('[automation-id="buy-sell-button-container-buy"] .price', row) | |
.innerText | |
); | |
const fromHighData = ((sell / max) * 100).toFixed(2); | |
const spreadData = `${(buy - sell).toFixed(2)} (${( | |
(100 * (buy - sell)) / | |
buy | |
).toFixed(2)}%)`; | |
if ($(".from-high", row).innerText !== fromHighData) { | |
$(".from-high", row).innerHTML = fromHighData; | |
} | |
if ($(".spread", row).innerText !== spreadData) { | |
$(".spread", row).innerHTML = spreadData; | |
} | |
}); | |
}, 500); | |
}; | |
window.addEventListener("urlchange", ({ url }) => onInit({ url })); | |
onInit({ url: location.href }); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment