Skip to content

Instantly share code, notes, and snippets.

@robin-hartmann
Last active April 13, 2024 14:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robin-hartmann/b70cde63135fd1b06472e9e6a89c30cc to your computer and use it in GitHub Desktop.
Save robin-hartmann/b70cde63135fd1b06472e9e6a89c30cc to your computer and use it in GitHub Desktop.
UserScript to copy a transaction activity from Scalable Capital
// ==UserScript==
// @name Copy Activity
// @namespace https://robin-hartmann.com/
// @version 1.0.9
// @description Copy Activity
// @author robin-hartmann
// @match https://de.scalable.capital/*
// @grant none
// @noframes
// ==/UserScript==
'use strict';
(function () {
window.addEventListener('copy', async (e) => {
if (!window.getSelection().isCollapsed) {
return;
}
try {
const activity = await getActivity();
if (!activity) {
return;
}
e.preventDefault();
e.clipboardData.setData('text/plain', activity);
} catch (error) {
alert('error while copying activity:\n' + error);
}
});
})();
async function getActivity() {
const sellSvg = document.querySelectorAll('svg[data-testid="icon-SELL"]')[0];
const buySvg = document.querySelectorAll('svg[data-testid="icon-BUY"],svg[data-testid="icon-SAVINGS_PLAN"]')[0];
const type = sellSvg ? 'sell' : buySvg ? 'buy' : undefined;
if (!type) {
return undefined;
}
// TODO assert exactly one match
const shareLink = document.querySelectorAll('.MuiDialog-container a')[0];
const shareUrl = new URL(shareLink.href);
const shareName = shareUrl.searchParams.get('isin');
const shareCountSpan = document.querySelectorAll(
'[data-testid="value-Ausgeführte Stückzahl"] span'
)[0];
const shareCount = shareCountSpan.textContent
.trim()
.replaceAll('.', '')
.replace(',', '.');
const shareValueDiv = document.querySelectorAll(
'div[data-testid="value-Ausführungspreis"]'
)[0];
// TODO assert none to multiple '.', exactly one ',', exactly one '€'
const shareValue = shareValueDiv.textContent
.trim()
.replaceAll('.', '')
.replace(',', '.')
.replace(' €', '');
const totalValueDiv = document.querySelectorAll(
'div[data-testid="value-Kurswert"]'
)[0];
const totalValue = totalValueDiv.textContent
.trim()
.replaceAll('.', '')
.replace(',', '.')
.replace(' €', '');
const feesDiv = document.querySelectorAll(
'div[data-testid="value-Ordergebühr"]'
)[0];
const fees =
feesDiv?.textContent
.replaceAll('.', '')
.replace(',', '.')
.replace(' €', '') || '0.00';
const taxesDiv = document.querySelectorAll(
'div[data-testid="value-Steuern"]'
)[0];
const taxes =
taxesDiv?.textContent
.replaceAll('.', '')
.replace(',', '.')
.replace(' €', '') || '0.00';
const billDiv = document.querySelectorAll(
'div[data-testid="value-Wertpapierabrechnung"]'
)[0];
const executionConfirmedLikeDivs = document.querySelectorAll(
'.MuiGrid-container.MuiGrid-item > div.MuiGrid-root.MuiGrid-item'
);
const executionConfirmedDiv = Array.from(executionConfirmedLikeDivs).filter(
(value) => value.textContent === 'Ausführung bestätigt'
)[0];
const timestampDiv = executionConfirmedDiv.nextSibling;
const timestamp = new Date(
timestampDiv.textContent
.replace('Mär', 'Mar')
.replace('Mai', 'May')
.replace('Okt', 'Oct')
.replace('Dez', 'Dec')
);
const isoTimestamp = new Date(
timestamp.getTime() - timestamp.getTimezoneOffset() * 60 * 1000
);
const date = isoTimestamp.toISOString().split('T')[0];
const time = isoTimestamp.toISOString().split('T')[1].slice(0, 5);
const activity = {
type,
shareName,
shareCount,
shareValue,
totalValue,
fees,
taxes,
date,
time,
};
shareLink.style.color = 'green';
if (!billDiv && type === 'sell') {
alert("bill isn't available yet, taxes may be missing");
}
return JSON.stringify(activity, null, 2);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment