Scalable Capital Depot Export

Userscript, das den Download des aktuellen Depots bei Scalable Capital als CSV-Datei erlaubt.

// ==UserScript==
// @name         Scalable Capital Depot Export
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Userscript, das den Download des aktuellen Depots bei Scalable Capital als CSV-Datei erlaubt.
// @author       https://github.com/lime-scripts
// @license MIT
// @match        https://de.scalable.capital/**
// @icon         https://www.google.com/s2/favicons?sz=64&domain=scalable.capital
// @grant      GM_registerMenuCommand
// ==/UserScript==

(function () {
    'use strict';


    GM_registerMenuCommand("Export Depot CSV", function () {
        const positions = findPositions();
        const csv = toCsv(positions);
        downloadCsv(csv, "ScalableNeu_depot_export_" + (new Date().toISOString().split(".")[0]) + ".csv");
    });


    function isObject(obj) {
        return typeof obj === 'object' && obj !== null;
    }


    function findPositions() {

        let resultsMap = {};

        function visit(node) {
            if (!node) {
                return;
            }
            else if (Array.isArray(node)) {
                for (const subNode of node) {
                    visit(subNode);
                }
            }
            else if (isObject(node)) {
                if (node["isin"] && node["inventory"]) {
                    resultsMap[node["isin"]] = { ...node };
                }
                else {
                    for (const key of Object.keys(node)) {
                        if (["children", "props", "security", "items"].includes(key) || key.startsWith("__reactProps")) {
                            visit(node[key]);
                        }
                    }
                }
            }
            if (node.childNodes) {
                node.childNodes.forEach(n => { visit(n); });
            }
        }

        visit(document.body);
        const positions = Array.from(Object.values(resultsMap)).map(pos => ({ isin: pos.isin, name: pos.name, amount: pos.inventory.position?.filled, price: pos.inventory.position?.fifoPrice, data: pos }));
        positions.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
        console.log(positions);
        console.log(positions.length, "Positionen");
        return positions;
    }


    function toCsv(rows) {
        const SEP = ";";
        const LINE_SEP = "\n";
        const csvRows = [];
        for (let i = -1; i < rows.length; i++) {
            const row = rows[i];
            const csvRow = Array(4).fill("");
            csvRow[0] = i >= 0 ? row.name : "Name";
            csvRow[1] = i >= 0 ? row.isin : "ISIN";
            csvRow[2] = i >= 0 ? "" + row.amount : "Anzahl";
            csvRow[3] = i >= 0 ? "" + row.price : "Preis";
            csvRows.push(csvRow);
        }
        const csv = csvRows.map(row => row.map(val => val.replaceAll(SEP, "")).join(SEP)).join(LINE_SEP);
        return csv;
    }


    function toCsvBaader(rows) {
        const SEP = ";";
        const LINE_SEP = "\n";
        const COL_COUNT = 63;
        const HEADER = "XXX-LFDNR;XXX-SNR;XXX-PFNR;XXX-DENR;XXX-DENR-EXT;XXX-DEPSPERR;XXX-WPBEZK;XXX-WPBEZ1;XXX-WPBEZ2;XXX-WPBEZ3;XXX-WPBEZ4;XXX-WPNR;XXX-WPNRID-D;XXX-WPNRID-DBOE;XXX-KONTNR;XXX-KONTRAKT;XXX-DATFAELL;XXX-STRIKE;XXX-KZCP;XXX-SERIE;XXX-NOTIER;XXX-WHG;XXX-NW-M;XXX-NWSP-M;XXX-EINKURS-M;XXX-AKTKURS-M;XXX-AKTKURSD-M;XXX-KW-M;XXX-KWB-M;XXX-ABGR-M;XXX-ABGRB-M;XXX-FAKTORI;XXX-FAKTORP;XXX-EINKURSB-M;XXX-KONTBEZ;XXX-DEPOTLANG;XXX-NAME1;XXX-NAME2;XXX-MONJAHR;XXX-KURSB;XXX-KFAKTOR;XXX-BEWMET;XXX-PRAENOT;XXX-TUVIP;XXX-WPENDF;XXX-PFBEZ1;XXX-PFBEZ2;XXX-UNDERLYING;XXX-DATUM;XXX-VVSNR;XXX-WPART;XXX-UNDERU;XXX-BLOOMBERGK;XXX-VERFALLSTAG;XXX-VERSIONSNUMM;XXX-AUSUEBUNG;XXX-NWJUR-M;XXX-UTIPOS;XXX-KURSEIND-M;XXX-KWEEUR-M;XXX-KWBEL;XXX-KWBELB";
        const csvRows = [];
        let rowCounter = 0;
        for (const row of rows) {
            rowCounter++;
            const csvRow = Array(COL_COUNT).fill("");
            csvRow[0] = "" + rowCounter;
            csvRow[7] = row.name;
            csvRow[11] = row.isin;
            csvRow[22] = ("" + row.amount).replaceAll(".", ",");
            csvRow[24] = ("" + row.price).replaceAll(".", ",");
            csvRows.push(csvRow);
        }
        const csv = HEADER + LINE_SEP + csvRows.map(row => row.map(val => val.replaceAll(SEP, "")).join(SEP)).join(LINE_SEP);
        return csv;
    }


    function downloadCsv(csv, filename) {
        const data = "data:text/csv;charset=utf-8," + csv;
        const link = document.createElement("a");
        link.setAttribute("href", encodeURI(data));
        link.setAttribute("download", filename);
        document.body.appendChild(link);
        link.click();
    }

})();