Skip to content

Instantly share code, notes, and snippets.

@ackvf
Last active April 13, 2022 16:26
Show Gist options
  • Save ackvf/c5fe6ad0663b82ad5dca6d16da727e84 to your computer and use it in GitHub Desktop.
Save ackvf/c5fe6ad0663b82ad5dca6d16da727e84 to your computer and use it in GitHub Desktop.
eToro scripts

How to use this?

enable balance

Create new Bookmark

add new bookmark page

Edit Bookmark details

  • Name: for example "Calculate Balance | eToro"
  • URL: Copy line 4 from bookmarklet.js
    The one starting with:

    javascript:eval(atob('ewogIC8vIG1hZ...

edit bookmark

See the new Bookmarklet

preview bookmark

Use Bookmarklet in a Copy Portfolio

Open Portfolio and select a Copy

portfolio / open copy

Click on the Bookmarklet and watch the new elements appear

enable balance

Reload page to remove it

PS

It is also possible to view the available balance when you press Remove Funds. Don't confirm the dialog though! Press the close button.

Remove Funds

function enhance(){{
// made by Qwerty https://gist.github.com/ackvf/c5fe6ad0663b82ad5dca6d16da727e84
// calculation based on https://www.etoro.com/posts/0__entry__c1253723-6eaa-4f3e-a1d6-475078a152c9?utm_medium=Direct&utm_source=55714&utm_content=0&utm_serial=SocialSharePostcopyLink_5522996&utm_campaign=SocialSharePostcopyLink_5522996&utm_term=
const format = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format
const parse = (_, el) => parseFloat(el.innerText.replaceAll(/[$,]/g, ''))
const cell = (value, title = '', valueSpecial = '') =>
`<div class="ui-table-cell${value < 0 ? ' negative' : ''}">${
value !== undefined || title
? ` <div class="rate-value">${value !== undefined ? format(value) : valueSpecial}</div>
<div class="rate-subvalue">${title}</div>`
: ''
}</div>`
const message = 'Report errors to https://gist.github.com/ackvf/c5fe6ad0663b82ad5dca6d16da727e84'
const thrower = (name) => { throw TypeError(`Expected value for "${name}" is missing. ${message}`) }
const check = ({rest, ...args}) => {
Object.entries(args).forEach(([n, v]) => (v === undefined) && thrower(n))
if (rest && rest.length) throw TypeError(`Too many parameters. ${message}`)
}
const th = $('div.w-portfolio-table-hat')
const tb = $('ui-table-body')
const tr = $('.ui-table-row.trade-row')
const [initial, inout, _pl_usd, value, ...rest1] = th.find('.i-portfolio-table-header-cell-value').map(parse)
check({initial, inout, _pl_usd, value, rest: rest1})
const [invested, pl_usd, pl_percent, refunds, pl_usd_closed, ...rest2] = tr.find('.rate-value').map(parse)
check({invested, pl_usd, pl_percent, refunds, pl_usd_closed, rest: rest2})
const deposit = initial + inout
const allocated = deposit + refunds + pl_usd_closed
const available = allocated - invested
const available_percent = available / allocated
const t = tr.clone() // trade row
const b = $(tr[0]).clone() // balance row
th.css('display', 'block')
tb.css('padding-bottom', 270)
th.parent().append(b, t)
b.find('.table-first-name').text('BALANCE')
b.find('.ui-table-body-slot')
.empty()
.append(
cell(),
cell(deposit, 'DEPOSIT'),
cell(allocated, 'ALLOCATED'),
cell(available, 'AVAILABLE ($)'),
cell(undefined, 'AVAILABLE (%)', `${Math.round(available_percent * 10000) / 100}%`)
)
}}
/*
// The next line is the code for the Bookmarklet
javascript:eval(atob('ewogIC8vIG1hZGUgYnkgUXdlcnR5IGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL2Fja3ZmL2M1ZmU2YWQwNjYzYjgyYWQ1ZGNhNmQxNmRhNzI3ZTg0CiAgLy8gY2FsY3VsYXRpb24gYmFzZWQgb24gaHR0cHM6Ly93d3cuZXRvcm8uY29tL3Bvc3RzLzBfX2VudHJ5X19jMTI1MzcyMy02ZWFhLTRmM2UtYTFkNi00NzUwNzhhMTUyYzk/dXRtX21lZGl1bT1EaXJlY3QmdXRtX3NvdXJjZT01NTcxNCZ1dG1fY29udGVudD0wJnV0bV9zZXJpYWw9U29jaWFsU2hhcmVQb3N0Y29weUxpbmtfNTUyMjk5NiZ1dG1fY2FtcGFpZ249U29jaWFsU2hhcmVQb3N0Y29weUxpbmtfNTUyMjk5NiZ1dG1fdGVybT0KICBjb25zdCBmb3JtYXQgPSBuZXcgSW50bC5OdW1iZXJGb3JtYXQoJ2VuLVVTJywgeyBzdHlsZTogJ2N1cnJlbmN5JywgY3VycmVuY3k6ICdVU0QnIH0pLmZvcm1hdAogIGNvbnN0IHBhcnNlID0gKF8sIGVsKSA9PiBwYXJzZUZsb2F0KGVsLmlubmVyVGV4dC5yZXBsYWNlQWxsKC9bJCxdL2csICcnKSkKICBjb25zdCBjZWxsID0gKHZhbHVlLCB0aXRsZSA9ICcnLCB2YWx1ZVNwZWNpYWwgPSAnJykgPT4gCiAgICBgPGRpdiBjbGFzcz0idWktdGFibGUtY2VsbCR7dmFsdWUgPCAwID8gJyBuZWdhdGl2ZScgOiAnJ30iPiR7CiAgICAgIHZhbHVlICE9PSB1bmRlZmluZWQgfHwgdGl0bGUKICAgICAgPyBgIDxkaXYgY2xhc3M9InJhdGUtdmFsdWUiPiR7dmFsdWUgIT09IHVuZGVmaW5lZCA/IGZvcm1hdCh2YWx1ZSkgOiB2YWx1ZVNwZWNpYWx9PC9kaXY+CiAgICAgICAgICA8ZGl2IGNsYXNzPSJyYXRlLXN1YnZhbHVlIj4ke3RpdGxlfTwvZGl2PmAKICAgICAgOiAnJwogICAgfTwvZGl2PmAKICBjb25zdCBtZXNzYWdlID0gJ1JlcG9ydCBlcnJvcnMgdG8gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vYWNrdmYvYzVmZTZhZDA2NjNiODJhZDVkY2E2ZDE2ZGE3MjdlODQnCiAgY29uc3QgdGhyb3dlciA9IChuYW1lKSA9PiB7IHRocm93IFR5cGVFcnJvcihgRXhwZWN0ZWQgdmFsdWUgZm9yICIke25hbWV9IiBpcyBtaXNzaW5nLiAke21lc3NhZ2V9YCkgfQogIGNvbnN0IGNoZWNrID0gKHtyZXN0LCAuLi5hcmdzfSkgPT4gewogICAgT2JqZWN0LmVudHJpZXMoYXJncykuZm9yRWFjaCgoW24sIHZdKSA9PiAodiA9PT0gdW5kZWZpbmVkKSAmJiB0aHJvd2VyKG4pKQogICAgaWYgKHJlc3QgJiYgcmVzdC5sZW5ndGgpIHRocm93IFR5cGVFcnJvcihgVG9vIG1hbnkgcGFyYW1ldGVycy4gJHttZXNzYWdlfWApCiAgfQoKICBjb25zdCB0aCA9ICQoJ2Rpdi53LXBvcnRmb2xpby10YWJsZS1oYXQnKQogIGNvbnN0IHRiID0gJCgndWktdGFibGUtYm9keScpCiAgY29uc3QgdHIgPSAkKCcudWktdGFibGUtcm93LnRyYWRlLXJvdycpCgoKICBjb25zdCBbaW5pdGlhbCwgaW5vdXQsIF9wbF91c2QsIHZhbHVlLCAuLi5yZXN0MV0gPSB0aC5maW5kKCcuaS1wb3J0Zm9saW8tdGFibGUtaGVhZGVyLWNlbGwtdmFsdWUnKS5tYXAocGFyc2UpCiAgY2hlY2soe2luaXRpYWwsIGlub3V0LCBfcGxfdXNkLCB2YWx1ZSwgcmVzdDogcmVzdDF9KQogIGNvbnN0IFtpbnZlc3RlZCwgcGxfdXNkLCBwbF9wZXJjZW50LCByZWZ1bmRzLCBwbF91c2RfY2xvc2VkLCAuLi5yZXN0Ml0gPSB0ci5maW5kKCcucmF0ZS12YWx1ZScpLm1hcChwYXJzZSkKICBjaGVjayh7aW52ZXN0ZWQsIHBsX3VzZCwgcGxfcGVyY2VudCwgcmVmdW5kcywgcGxfdXNkX2Nsb3NlZCwgcmVzdDogcmVzdDJ9KQoKICBjb25zdCBkZXBvc2l0ID0gaW5pdGlhbCArIGlub3V0CiAgY29uc3QgYWxsb2NhdGVkID0gZGVwb3NpdCArIHJlZnVuZHMgKyBwbF91c2RfY2xvc2VkCiAgY29uc3QgYXZhaWxhYmxlID0gYWxsb2NhdGVkIC0gaW52ZXN0ZWQKICBjb25zdCBhdmFpbGFibGVfcGVyY2VudCA9IGF2YWlsYWJsZSAvIGFsbG9jYXRlZAoKCiAgY29uc3QgdCA9IHRyLmNsb25lKCkgLy8gdHJhZGUgcm93CiAgY29uc3QgYiA9ICQodHJbMF0pLmNsb25lKCkgLy8gYmFsYW5jZSByb3cKICB0aC5jc3MoJ2Rpc3BsYXknLCAnYmxvY2snKQogIHRiLmNzcygncGFkZGluZy1ib3R0b20nLCAyNzApCiAgdGgucGFyZW50KCkuYXBwZW5kKGIsIHQpCgogIGIuZmluZCgnLnRhYmxlLWZpcnN0LW5hbWUnKS50ZXh0KCdCQUxBTkNFJykKICBiLmZpbmQoJy51aS10YWJsZS1ib2R5LXNsb3QnKQogICAgLmVtcHR5KCkKICAgIC5hcHBlbmQoCiAgICAgIGNlbGwoKSwKICAgICAgY2VsbChkZXBvc2l0LCAnREVQT1NJVCcpLAogICAgICBjZWxsKGFsbG9jYXRlZCwgJ0FMTE9DQVRFRCcpLAogICAgICBjZWxsKGF2YWlsYWJsZSwgJ0FWQUlMQUJMRSAoJCknKSwKICAgICAgY2VsbCh1bmRlZmluZWQsICdBVkFJTEFCTEUgKCUpJywgYCR7TWF0aC5yb3VuZChhdmFpbGFibGVfcGVyY2VudCAqIDEwMDAwKSAvIDEwMH0lYCkKICAgICkKfQ=='))
*/
// To verify that the code above is indeed what is executed the bookmarklet, you can run this code and compare the output.
function toBase64Bookmarklet(fn){
const text = fn.toString()
const body = text.slice(text.indexOf('{') + 1, text.lastIndexOf('}'))
const bstring = `javascript:eval(atob('${btoa(body)}'))`
copy(bstring)
return bstring
}
function enhance() {} // <- replace this with the code of enhance above.
toBase64Bookmarklet(enhance) // returns javascript:eval(atob('ewogIC8vIG1hZGUgYnk...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment