text:
# betterKite

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.

In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to [<https://unlicense.org>](https://unlicense.org)

Very simple "userscript" which adds several features to kite.zerodha.com ui (screenshots below)
* See P&L and current value 'including' pledged stocks
* Automatically create SL order, then 'trail' and 'save profit' by just one click.
* 'Smart Limit' Place LIMIT orders at best offer/bid price.
* See quantities in lots
* See real profit based on currently holding quantity
* See possible Nifty monthly and weekly range based on INDIA VIX
* See realised p&l for the day
* Avoid quantity freeze limitation, breaks bigger order into multiple orders
* Group positions under strategies
* For a strategy, check how much margin can be freed by taking hedge buy positions
* See P&L for each strategy
* See margin utilised for each position or for a strategy
* Mark trades as reference trade or martingale
* See your ROI
* Get explanation of various rows in funds page
* Filter trades by expiry in sensibull pop-up
* Auto group strategy by scrip and expiry
* Auto filter Watch list for scrip in positions
* Highlight position when you click on scrip in Watch list
* Filter mis only, pe online or CE only positions
* For a strategy, see potentially how much margin can be released by buying hedge positions
![savemargin](https://dl.dropbox.com/s/aiacmoefjxdb35l/saveMargin.png?dl=0)
* Margin calculation is done by 2 methods. If you use 'baseket' method (this is default) then no further action needed but if youw ant to use 'Margin Calculator' method then you need to enable CORS. Choosing which method is available in settings.
* Check this image on enabling CORS:
![corsusage](https://dl.dropbox.com/s/mbktrw9dkqu4wl0/corsToggle.png?dl=0)

------
Contributors
- Amit
- Yuva (@rbcdev)

------

# Installation

Follow below mentioned steps
* Install [Tempermonkey](https://www.tampermonkey.net/) for your browser (works on all browsers. Tested on Chrome, Vivaldi, Edge). [Chrome extension link](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo)
* Open [this link](https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.user.js) in a new tab. Or copy paste <https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.user.js>
* Tampermonkey will automatically identify the file as userscript and give you option to install. (sell image below)
* ![tampermonkey install](https://dl.dropbox.com/s/khs3itzctu13ayw/tampermonkeyInstall.png?dl=0)
* Click on 'Install' button.
* You can verify the installation by clicking on Tampermonkey icon (next to address bar) and then go to 'Dashboard'. You should see 'betterKite' installed. (see below)
* ![dashboard menu](https://dl.dropbox.com/s/dv1reqb84mz00bm/dashboardmenuoption.png?dl=0)
* ![dashboard sample](https://dl.dropbox.com/s/blv2j9t8e6iohkt/dashboardSample.png?dl=0)
* Now just go to <https://kite.zerodha.com> (refresh the page if its already open) and start using.
* Verify that your version should be more than v2.00. Click on Tempermonkey icon, you will see version in the 'Reset Data (WARNING) menu item.
* Below plugin is required only if you want to use Margin Calculator methods* Install CORS plugin for your browser.
  * Firefox: https://addons.mozilla.org/en-US/firefox/addon/access-control-allow-origin/
  * Edge: https://microsoftedge.microsoft.com/addons/detail/allow-cors-accesscontro/bhjepjpgngghppolkjdhckmnfphffdag?hl=en-GB
  * chrome: https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf

------
# https://kite.zerodha.com/positions page
* Enable 'Auto SL order' feature from settings. You will get option to auto create SL order when placing a new order. Once SL order is placed, in pending order section you will get button to 'trali' or 'save profit'. Trail simply increases or decreases trigger price for 'trail points'. Save profit is used when market has moved in your favor and you want to protect profit, in this case trigger price of SL trigger pending order is moved to make atleast 'save profit points'.
![OrderScreen](https://dl.dropbox.com/s/x9kaheg26hdsuc0/sl_create_sl_order.png?dl=0)
![pendingORder](https://dl.dropbox.com/s/11vrrzch6bi9ysd/sl_trail_save_profit_btn.png?dl=0)
![settings](https://dl.dropbox.com/s/sbuqqclaerljpva/sl_order_settings.png?dl=0)
![help1](https://dl.dropbox.com/s/u83bs5ec9gdl2t8/sl_save_profit_help.png?dl=0)
![help2](https://dl.dropbox.com/s/ot3rjkhnvid0fee/sl_trail_help.png?dl=0)
* 'Smart Limit' Finds the best offer/bid price then ether adds or subtracts 0.05p (depending on buy or sell order) and places a LIMIT order.
![SmartLimit](https://dl.dropbox.com/s/b02kq0vjxscw3p2/smartLimit.png?dl=0)
* You can easily execute/exit quantities higher than freeze quantity. To enable quantity freeze go to settings. Once enabled, checkbox will show below 'Sell' or 'Buy' button, enable it and place orders
![QuantityFeeze](https://dl.dropbox.com/s/h55i2gkk6gj5y6j/qtyFreeze.png?dl=0)
![QuantityFeeze1](https://dl.dropbox.com/s/n4jd45bpzl6dh1p/qtySetings.png?dl=0)
![QuantityFeeze2](https://dl.dropbox.com/s/sc4xd7wsofz9zdp/qtySettings-2.png?dl=0)
* Click on quanity to see it in lots, click again to remove it ![lots](https://dl.dropbox.com/s/9p7sxvuu2kguqly/seeQuantityInLots.png?dl=0)
* Click on any of the pnl to enable/disable this feature ![pnl](https://dl.dropbox.com/s/7oql0zgzihvjsw4/seeRealProfit.png?dl=0)
* Step 1: Add Nifty 50 to Pin 1, click on Nifty value to see monthly range, click on change value to see weekly range
![Monthly](https://dl.dropbox.com/s/vr1x6b8llju46ng/monthlyRangeVix.png?dl=0)
![Weekly](https://dl.dropbox.com/s/l28p58dc2dr2w4u/WeeklyRangeVix.png?dl=0)
* Click on Total P&L to start seeting 'Realised P&L' ![realised](https://dl.dropbox.com/s/b6q7b6zn5jimkvd/realisedPnlForTheDay.png?dl=0)
* Strategies are grouped in 3 ways.
  * (1) Strategies are auto grouped by script name. So all INFY strategies will be auto grouped under 'INFY'
  * (2) Strategies are auto grouped by expiry.
  * (3) Manual / custom strategies.
* Once you select a strategy from the dropdown, only relevant positions will be shown.
![strategies](https://dl.dropbox.com/s/414mh3oqvx4ppf2/strategies.png?dl=0)

* ![strategies2](https://dl.dropbox.com/s/qjyok361dk9jo6c/strategies2.png?dl=0)
* You can also see strategy's P&L on the right side of dropdown.
* You can see MARGIN required for the 'group of positions' along with P&L on top.
* You can also see margin required for individual position at the bottom of the table.
* ![marginUsage](https://dl.dropbox.com/s/8cii9hr27ctqebc/marginCalculationUsage.png?dl=0)
* Tag your reference trades and martingales for easier identification. You can give custom name and color for easy identification
![referenceTags](https://dl.dropbox.com/s/i18bklcdebtagia/referenceTags.png?dl=0)
* Quickly see total P&L of 'selected' positions.
![addPositions](https://dl.dropbox.com/s/mvavj8njmt2xvtp/pnlAddition.png?dl=0)
* "Filter" button is added next to 1,2,3,4,5 watchlist. You can use this to filter your watchlist.
![watchlistFilter](https://dl.dropbox.com/s/5gf2paw5pk9you6/watchlistFilter.png?dl=0)
* Filter positions within the sensibull interface.
![zerodaSensibullAnalyze](https://dl.dropbox.com/s/rqzpo214j961x1u/zerodaSensibullAnalyze.png?dl=0)
![zerodhaSensibullFilters](https://dl.dropbox.com/s/1s91foyuaewkfed/zerodhaSensibullFilters.png?dl=0)

------
# https://kite.zerodha.com/holdings page
* See P&L and current value 'including' pledged stocks
![pledged](https://dl.dropbox.com/s/mnhx9efdjpo5d2m/pledgedPnl.png?dl=0)
* Group your holdings in 'categories' or 'tags'
* Small tag is shown next to your stock name indicating which category it belongs to.
![tags](https://dl.dropbox.com/s/ygk9id8c21b3mi8/HoldingsWithTags.png?dl=0)
* Filter stocks based on category. Once filter is applied only stocks in that category will be shown in 'watchlist' or 'orders' or 'holdings' screen
![header](https://dl.dropbox.com/s/zvefkb2pis0ygq4/headerWithTagSelector.png?dl=0)
* One stock can be in multiple categories.
* When you click on a stock in watchlist, if same stock is present in 'holdings', screen will be scrolled automatically bringing the stock in the middle and it will be highlighted for a few seconds.

------
# [Click on this link to learn how to use the tool to copy orders from one zerodha account to another.](https://github.com/amit0rana/betterOptionsTrading/blob/master/betterKiteCopyOrders.md)

------
# https://console.zerodha.com/reports/pnl page
Introduces a convenence link to see "current month's" F&O P&L
![usage](https://dl.dropbox.com/s/vhrh6qqf775kg9y/foThisMonth.png?dl=0)

------
# https://kite.zerodha.com/funds page
Introduced help text on each row explaining what different rows mean.
![usage](https://dl.dropbox.com/s/cjo8y462vxpp7iw/usageFundsHelpText.png?dl=0)

------
# Installation

Follow below mentioned steps
* Install [Tempermonkey](https://www.tampermonkey.net/) for your browser (works on all browsers. Tested on Chrome, Vivaldi, Edge). [Chrome extension link](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo)
* Open [this link](https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.user.js) in a new tab. Or copy paste <https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.user.js>
* Tampermonkey will automatically identify the file as userscript and give you option to install. (sell image below)
* ![tampermonkey install](https://dl.dropbox.com/s/khs3itzctu13ayw/tampermonkeyInstall.png?dl=0)
* Click on 'Install' button.
* You can verify the installation by clicking on Tampermonkey icon (next to address bar) and then go to 'Dashboard'. You should see 'betterKite' installed. (see below)
* ![dashboard menu](https://dl.dropbox.com/s/dv1reqb84mz00bm/dashboardmenuoption.png?dl=0)
* ![dashboard sample](https://dl.dropbox.com/s/blv2j9t8e6iohkt/dashboardSample.png?dl=0)
* Now just go to <https://kite.zerodha.com> (refresh the page if its already open) and start using.
* Verify that your version should be more than v2.00. Click on Tempermonkey icon, you will see version in the 'Reset Data (WARNING) menu item.

------
# How to use
* Once installation is done, go to 'holdings' page and you will see a dropdown next to the logo listing all your groups.
  * On holding pages, dropdown will show all the groups under which you want to keep your holding stocks
  * On Positions page, dropdown will show all the strategies.
* you can now filter 'positions' screen and 'holdings' screen based on your selection. Watchlist will also be filtered.
* When you click on watchlist row, if same stock is present in your holdings list, it will be highlighted and brought to focus.
* You can enabled/disable the filter dropdown by clicking on 'Position()' or 'Holdings()' heading.

------
# Steps for grouping your Holdings
* Refer to images below on how to use.
* Adding tag : Go to holdings section, click on + icon next to the stock name to add tags.
* Removing tag: Click on tag to remove it.
* Filtering stocks: Use the dropdown for filtering.
* ![how to use](https://dl.dropbox.com/s/tllta7nzcfl145a/holdingsHowToUse.gif?dl=0)
* ![add remove tags](https://dl.dropbox.com/s/nbjuxiu7yh9p51i/addingRemovingTags.gif?dl=0)

------
# Steps for marking trades as reference trades or base trade or martingales
* On the positions screen, each position has a + button, click on the button to add a tag. Important thing to keep in mind is the format of the tag.
* Tag should be formated as tagName.color for example: RT.red or BS.blue etc.

------
# Steps for custom strategies.
* Please see the animated gif below
![addingDeletingStrategies](https://dl.dropbox.com/s/23mec9h3zd8iolw/AddingDeletingStrategy.gif?dl=0)
(if you can't see the image above see this video: https://dl.dropbox.com/s/iay4ld2ibdx3t91/AddingDeletingStrategy.mp4?dl=0)

------
TODOs
* Instructions for [Violentmonkey](https://openuserjs.org/about/Violentmonkey-for-Chrome)
* Filtering on order page
* Let users disable analytics

Note
This script tracks following events for analytics. No data is passed/stored/tracked. This only tells that action was taken.
* Script loaded
* Script enabled/disabled
* Watchlist filter used
* Tag added
* Tag deleted
* Filter applied

[//]: # (SEBI MANDATORY DISCLAIMER AS REQUIRED BY SEBI)

[//]: # (KIND ATTENTION TO ALL USERS)

[//]: # (Disclaimer from Developers as per SEBI norms: Equity Investment are subject to 100%)
[//]: # (market risks. Refer your financial consultant advice before Investing. This code is)
[//]: # (only for Educational and Learning, Knowledge Purposes. Developers have no)
[//]: # (responsibility for your intended decision & financial losses. Keep calculated & always)
[//]: # (analyzed your cash osition and risk bearing capacity before following any advise.)
[//]: # (Stock market investments and using this script are VERY RISKY and using ths code, you )
[//]: # (agree that you understand the Market risks involved. Profits and Losses are part of Share market.)
[//]: # (Kindly understand and act wisely.)

[//]: # (Disclaimer/ disclosure)

[//]: # (👉This script does not provide any tips/recommendations/advice)
[//]: # (👉All features are only for education and learning purpose.)
[//]: # (👉Do Consult your financial advisor before taking trades or investment decisions)
[//]: # (👉Developers are not responsible for any financial losses)
[//]: # (👉Disclaimer/disclosure/terms and conditions applicable to all users of this script)

[//]: # (All the features are for education and learning purpose only👍)

repo_language: JavaScript

mime_type: text/plain


text:
// ==UserScript==
// @name         betterSharekhan
// @namespace    https://github.com/amit0rana/betterSharekhan
// @version      0.07
// @description  Introduces small features on top of newtrade.sharekhan.com
// @author       Amit
// @match        https://newtrade.sharekhan.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @require      https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js
// @require      https://raw.githubusercontent.com/kawanet/qs-lite/master/dist/qs-lite.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js
// @downloadURL  https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.user.js
// @updateURL    https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.meta.js
// ==/UserScript==

var context=window,options="{    anonymizeIp: true,    colorDepth: true,    characterSet: true,    screenSize: true,    language: true}";const hhistory=context.history,doc=document,nav=navigator||{},storage=localStorage,encode=encodeURIComponent,pushState=hhistory.pushState,typeException="exception",generateId=()=>Math.random().toString(36),getId=()=>(storage.cid||(storage.cid=generateId()),storage.cid),serialize=e=>{var t=[];for(var o in e)e.hasOwnProperty(o)&&void 0!==e[o]&&t.push(encode(o)+"="+encode(e[o]));return t.join("&")},track=(e,t,o,n,i,a,r)=>{const c="https://www.google-analytics.com/collect",s=serialize({v:"1",ds:"web",aip:options.anonymizeIp?1:void 0,tid:"UA-176741575-1",cid:getId(),t:e||"pageview",sd:options.colorDepth&&screen.colorDepth?`${screen.colorDepth}-bits`:void 0,dr:doc.referrer||void 0,dt:doc.title,dl:doc.location.origin+doc.location.pathname+doc.location.search,ul:options.language?(nav.language||"").toLowerCase():void 0,de:options.characterSet?doc.characterSet:void 0,sr:options.screenSize?`${(context.screen||{}).width}x${(context.screen||{}).height}`:void 0,vp:options.screenSize&&context.visualViewport?`${(context.visualViewport||{}).width}x${(context.visualViewport||{}).height}`:void 0,ec:t||void 0,ea:o||void 0,el:n||void 0,ev:i||void 0,exd:a||void 0,exf:void 0!==r&&!1==!!r?0:void 0});if(nav.sendBeacon)nav.sendBeacon(c,s);else{var d=new XMLHttpRequest;d.open("POST",c,!0),d.send(s)}},tEv=(e,t,o,n)=>track("event",e,t,o,n),tEx=(e,t)=>track(typeException,null,null,null,null,e,t);hhistory.pushState=function(e){return"function"==typeof history.onpushstate&&hhistory.onpushstate({state:e}),setTimeout(track,options.delay||10),pushState.apply(hhistory,arguments)},track(),context.ma={tEv:tEv,tEx:tEx};

window.jQ=jQuery.noConflict(true);
const VERSION = "v0.07";
const GMPositionsName = "BK_POSITIONS";
const D_LEVEL = D_LEVEL_DEBUG;

const allDOMPaths = {
    //document.querySelector("#sort > tbody > tr:nth-child(2)")
    PathForPositions : "#sort > tbody > tr"
};

const positions = initPositions();

//main();

function initPositions() {
    var defaultPositions = {
    };
    var positions = GM_getValue(GMPositionsName,defaultPositions);

    GM_registerMenuCommand("Add to or Create new Strategy", function() {
        var selectedPositions = jQ(allDOMPaths.PathForPositions).filter(':has(:checkbox:checked)');
        info("selected ps "+ selectedPositions.length);

        var storedStrategies = GM_getValue(GMPositionsName,defaultPositions);
        var keys = [];
        for (var k in storedStrategies) {
            keys.push(k);
        }

        var msg = '';
        if (selectedPositions.length <= 0) {
            alert('Please select atleast one position to be added to a strategy');
            return;
            //msg = 'You have selected '+selectedPositions.length+' positions. Please provide name of the strategy. \n\nNOTE: (1) If strategy name exists then position will be added to the group. \n(2) If this is new name then new strategy will be created.';
        }

        msg = 'Please tell your strategy name:\n\nNOTE: (1) If there is an existing strategy with same name then we will add selected position to same strategy. \n(2)If there is no strategy with this name then we will create a new one.\n\nYour existing strategies are: '+keys.toString();

        var strategyName = prompt(msg);
        if (strategyName == null) return;
        strategyName = strategyName.toUpperCase();

        if(keys.includes(strategyName)) {
            var confirmAdd = confirm("This message is to confirm that there was no typo in strategy name.\n\nYou are adding to an **EXISTING** strategy.");
            if (confirmAdd == false) return;
        } else {
            var confirmAdd = confirm("This message is to confirm that there was no typo in strategy name.\n\nYou are adding to a **NEW** strategy.");
            if (confirmAdd == false) return;
        }

        var positionArray = [];
        selectedPositions.each(function(rowIndex) {

            var span = jQ(this).find("td:nth-child(2) > span");
            var params = JSON.parse(jQ(span).attr('params'));
            debug(params);
            var text = params.detail.contract;
            positionArray.push(text);
        });

        debug(JSON.stringify(storedStrategies));

        if(keys.includes(strategyName)) {
            storedStrategies[strategyName] = storedStrategies[strategyName].concat(positionArray);
        } else {
            storedStrategies[strategyName] = positionArray;
        }
        debug(JSON.stringify(storedStrategies));

        GM_setValue(GMPositionsName,storedStrategies);
        window.location.reload();
    }, "s");

    GM_registerMenuCommand("Delete A Strategy", function() {
        var storedStrategies = GM_getValue(GMPositionsName,defaultPositions);
        var keys = [];
        for (var k in storedStrategies) {
            keys.push(k);
        }

        var strategyToDelete = prompt("Please specify which strategy do you want to delete: " + keys.toString());
        if (strategyToDelete == null) return;
        strategyToDelete = strategyToDelete.toUpperCase();

        if(keys.includes(strategyToDelete)) {
            var confirmDelete = confirm("Deleting strategy " + strategyToDelete + "\n\nNOTE: Deletion does not have any impact on the Positions, this will simply delete strategy from the dropdown. Please confirm, name is correct?");
            if (confirmDelete == false) return;

            debug(JSON.stringify(storedStrategies));
            delete storedStrategies[strategyToDelete];
            debug(JSON.stringify(storedStrategies));
            GM_setValue(GMPositionsName,storedStrategies);

            window.location.reload();
        } else {
            alert('You do not have any custom strategy with this name. No action taken. Input you gave was: ' + strategyToDelete);
        }

    }, "d");

    return positions;
}

function createPositionsDropdown() {
    var selectBox = document.createElement("SELECT");
    selectBox.id = "tagSelectorP";
    selectBox.classList.add("randomClassToHelpHide");
    selectBox.style="margin: 15px 0;margin-top: 15px;margin-right: 0px;margin-bottom: 15px;margin-left: 0px;font-size: 12px;background-color: var(--color-bg-default)"

    var option = document.createElement("option");
    option.text = "All Positions";
    option.value= "All";
    selectBox.add(option);

    var userGeneratedGroups = document.createElement("optgroup");
    userGeneratedGroups.text = "---USER GROUPS---";
    userGeneratedGroups.label = "---USER GROUPS---";

    selectBox.addEventListener("change", function() {
        tEv("sharekhan","positions","filter","");
        var selectedGroup = this.value;

        info("Group selected: " + selectedGroup);
        var selectedPositions = positions[selectedGroup];

            //START work on Positions AREA
            var allPositionsRow = jQ(allDOMPaths.PathForPositions);
            info('found positions row: ' + allPositionsRow.length);
            allPositionsRow.show();

            var stocksInList = [];

            //logic to hide the rows in positions table not in our list
            var countPositionsDisplaying = 0;
            allPositionsRow.addClass("allHiddenRows");

            var tdymtm = 0.0;
            var settledmtm = 0.0;
            var totalmtm = 0.0;
            var tdybpl = 0.0;
            var totalbpl = 0.0;
            var selection = [];

            allPositionsRow.each(function(rowIndex) {
                try {
                    var matchFound = false;
                    var span = jQ(this).find("td:nth-child(2) > span");
                    var params = JSON.parse(jQ(span).attr('params'));
                    debug(params);

                    var paramSplit = params.detail.contract.split(" ");

                    var contract = params.detail.contract;
                    var exp =  paramSplit[1];
                    var scrip = paramSplit[0];

                    if (selectedGroup === "All") {
                        matchFound = true;
                        debug('all so showing');
                    } else {
                        if (selectedPositions) {
                            matchFound = selectedPositions.includes(contract);
                            if (matchFound) {
                                debug('custom group found');
                            } else {
                                debug('custom group present but no match');
                            }
                        } else {
                            debug('custom group not found');
                            matchFound = (selectedGroup==exp)?true:false;
                            if (!matchFound) {
                                debug('expiry no match');
                                matchFound = (selectedGroup==scrip)?true:false;

                                if (matchFound) {
                                    debug('scrip match');
                                } else {
                                    debug('no match at all');
                                }
                            } else {
                                debug('exiry match');
                            }
                        }
                    }

                    if (matchFound) {
                        tdymtm += parseFloat(params.detail.tdymtm);
                        settledmtm += parseFloat(params.detail.settledmtm);
                        totalmtm += parseFloat(params.detail.totalmtm);
                        tdybpl += parseFloat(params.detail.tdybpl);
                        totalbpl += parseFloat(params.detail.totalbpl);
                    } else {
                        jQ(this).hide();
                    }
                } catch(err) {
                }
            //END work on Positions AREA
        });

        //document.querySelector("#sort > tbody > tr:nth-child(5) > td:nth-child(16)") today mtm, settled mtm, total mtm, today bpl, total bpm
        updatePnlHtml(allPositionsRow, tdymtm, settledmtm, totalmtm, tdybpl, totalbpl);

        return this;
    });

    //add options to the positions select drop down
    for(var key in positions){
        option = document.createElement("option");
        option.text = key;
        option.value = key;
        jQ(userGeneratedGroups).append(option);
    };

    selectBox.add(userGeneratedGroups);
    return selectBox;
}

function updatePnlHtml(allPositionsRow, tdymtm, settledmtm, totalmtm, tdybpl, totalbpl) {
    jQ("#sort > thead > tr > th:nth-child(16)").attr("style","width:8%");
    jQ("#sort > thead > tr > th:nth-child(17)").attr("style","width:8%");
    jQ("#sort > thead > tr > th:nth-child(18)").attr("style","width:8%");
    jQ("#sort > thead > tr > th:nth-child(19)").attr("style","width:8%");
    jQ("#sort > thead > tr > th:nth-child(20)").attr("style","width:8%");

    jQ('#idtodaymtm').remove();
    jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(16)`).append(`<span id='idtodaymtm'><br /><b style="font-size: 11px;background: pink;">${formatter.format(tdymtm)}</b></span>`);

    jQ('#idsettledmtm').remove();
    jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(17)`).append(`<span id='idsettledmtm'><br /><b style="font-size: 11px;background: pink;">${formatter.format(settledmtm)}</b></span>`);

    jQ('#idtotalmtm').remove();
    jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(18)`).append(`<span id='idtotalmtm'><br /><b style="font-size: 11px;background: pink;">${formatter.format(totalmtm)}</b></span>`);

    jQ('#idtdybpl').remove();
    jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(19)`).append(`<span id='idtdybpl'><br /><b style="font-size: 11px;background: pink;">${formatter.format(tdybpl)}</b></span>`);

    jQ('#idtotalbpl').remove();
    jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(20)`).append(`<span id='idtotalbpl'><br /><b style="font-size: 11px;background: pink;">${formatter.format(totalbpl)}</b></span>`);
}

waitForKeyElements("body > div:nth-child(6) > ui-view > ui-view > turnover > div > div.card > div.table-responsive.ng-scope > div:nth-child(1) > div.col-xs-1 > button",showPositionDropdown);

function showPositionDropdown(retry = true) {
    debug('showPositionDropdown');

    var allPositionsRow = jQ(allDOMPaths.PathForPositions);

    if (allPositionsRow.length < 1) {
        debug('sleeping as couldnt find positions');
        //TODO if no positions this will cause loop
        if (retry) {
            setTimeout(function(){ showPositionDropdown(false); }, 1000);
        }
        return;
    }

    var positionGroupdropdown;
    debug(jQ('#tagSelectorP').exists());
    if (jQ('#tagSelectorP').exists()) {
        positionGroupdropdown = jQ('#tagSelectorP');
    } else {
        positionGroupdropdown = createPositionsDropdown();
    }

    var lastC = positionGroupdropdown.lastChild;
    debug(lastC);
    if (lastC && lastC.id == "randomForDeleteOptGroupE") {
        positionGroupdropdown.removeChild(lastC);
    }

    lastC = positionGroupdropdown.lastChild;
    debug(lastC);
    if (lastC && lastC.id == "randomForDeleteOptGroup") {
        positionGroupdropdown.removeChild(lastC);
    }

    var optGrp = document.createElement("optgroup");
    optGrp.text = "---SCRIPT WISE---";
    optGrp.label = "---SCRIPT WISE---";
    optGrp.id = "randomForDeleteOptGroup";

    var optGrpExpiry = document.createElement("optgroup");
    optGrpExpiry.text = "---EXPIRY WISE---";
    optGrpExpiry.label = "---EXPIRY WISE---";
    optGrpExpiry.id = "randomForDeleteOptGroupE";

    var arrForUnique = [];
    var uniqueExpiryArray = [];
    allPositionsRow.each(function(rowIndex) {

        var span = jQ(this).find("td:nth-child(2) > span");
        debug(span);
       try{
        var params = JSON.parse(jQ(span).attr('params'));
        /*
        {
            "ex": "NSEFO",
            "scripcode": "NIFTY 09Dec2021 CE 19000",
            "token": "43403",
            "detail": {
                "netqty": 650,
                "netrate": 3.96,
                "invsttype": "INVST",
                "contract": "NIFTY 09Dec2021 CE 19000",
                "scripcode": "NIFTY 09Dec2021 CE 19000",
                "ltp": "2.30",
                "pricefactor": 1,
                "diff": "-0.45",
                "prevcloserate": "2.75",
                "totalbpl": "0.00",
                "acctype": "Bullish",
                "sellqty": 0,
                "buyrate": 2.4,
                "token": "43403",
                "settledmtm": "-804.00",
                "openrate": 4.09,
                "netopenmtm": -803.9999999999999,
                "buyqty": 50,
                "totalmtm": "-1079.00",
                "tdymtm": "-275.00",
                "openqty": 600,
                "exchange": "NSEFO",
                "sellrate": 0,
                "tdybpl": "0.00"
            }
        }
        */
        debug(params);
        debug(params.scripcode);

        var arr = params.detail.contract.split(" ");

        //creating auto generated script wise grouping
        // var ts = params.scripcode;
        var ts = arr[0];
        if (!arrForUnique.includes(ts)) {
            var option = document.createElement("option");
            option.text = ts;
            option.value = ts;
            jQ(optGrp).append(option);
            arrForUnique.push(ts);
        }

        //creating auto generated expiry wise grouping

        //NIFTY 17Jun2021 PE 14500
        var ex = arr[1];
        if (!uniqueExpiryArray.includes(ex)) {
            var option2 = document.createElement("option");
            option2.text = ex;
            option2.value = ex;
            jQ(optGrpExpiry).append(option2);
            uniqueExpiryArray.push(ex);
        }
       } catch(err) {
           //debug(err);
       }

    });

    positionGroupdropdown.add(optGrp);
    positionGroupdropdown.add(optGrpExpiry);

    var ourDiv = document.createElement("div");
    ourDiv.classList.add("randomClassToHelpHide");
    ourDiv.classList.add("tagSelectorStyle");
    ourDiv.classList.add("col-xs-2");
    ourDiv.id ='ourDiv';

    ourDiv.appendChild(positionGroupdropdown);
    //document.querySelector("body > div:nth-child(6) > ui-view > ui-view > turnover > div > div.card > div.table-responsive.ng-scope > div:nth-child(1) > div.col-xs-1 > button")
    jQ("body > div:nth-child(6) > ui-view > ui-view > turnover > div > div.card > div.table-responsive.ng-scope > div:nth-child(1) > div.col-xs-1").before(ourDiv);

//    simulateSelectBoxEvent();

    var target = jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(18) > b`)[0];
    debug('--------------------------------------------------------');

    debug(target);
    var g_positionsPnlObserver = new MutationObserver(function (mutations) {
        var st = null;
        mutations.forEach(function (mutation) {
            if (mutation.type == "characterData") {
                debug("pnl changed");
                updatePnl();
            }
        });
    });

    // Configuration of the observer:
    var config = {
        attributes:true,
        characterData: true,
        subtree: true
    };

    // Pass in the target node, as well as the observer options
    g_positionsPnlObserver.observe(target, config);

}

function updatePnl() {
    debug('pnl changed');

    var allPositionsRow = jQ(allDOMPaths.PathForPositions);
    var allVisibleRows = jQ(allDOMPaths.PathForPositions + ":visible");

    var tdymtm = 0.0;
    var settledmtm = 0.0;
    var totalmtm = 0.0;
    var tdybpl = 0.0;
    var totalbpl = 0.0;

    allVisibleRows.each(function(rowIndex) {
        var span = jQ(this).find("td:nth-child(2) > span");
       try{
            var params = JSON.parse(jQ(span).attr('params'));

            tdymtm += parseFloat(params.detail.tdymtm);
            settledmtm += parseFloat(params.detail.settledmtm);
            totalmtm += parseFloat(params.detail.totalmtm);
            tdybpl += parseFloat(params.detail.tdybpl);
            totalbpl += parseFloat(params.detail.totalbpl);

        } catch(err) {
        }
    });

    //document.querySelector("#sort > tbody > tr:nth-child(5) > td:nth-child(16)") today mtm, settled mtm, total mtm, today bpl, total bpm
    updatePnlHtml(allPositionsRow, tdymtm, settledmtm, totalmtm, tdybpl, totalbpl);

}

jQ.fn.exists = function () {
    return this.length !== 0;
}

tEv("sharekhan","visit","main","");

function waitForKeyElements (
    selectorTxt,    /* Required: The jQuery selector string that
                        specifies the desired element(s).
                    */
    actionFunction, /* Required: The code to run when elements are
                        found. It is passed a jNode to the matched
                        element.
                    */
    bWaitOnce,      /* Optional: If false, will continue to scan for
                        new elements even after the first match is
                        found.
                    */
    iframeSelector  /* Optional: If set, identifies the iframe to
                        search.
                    */
) {
    var targetNodes, btargetsFound;

    if (typeof iframeSelector == "undefined")
        targetNodes     = jQ(selectorTxt);
    else
        targetNodes     = jQ(iframeSelector).contents ()
                                        .find (selectorTxt);

    if (targetNodes  &&  targetNodes.length > 0) {
        btargetsFound   = true;
        /*--- Found target node(s).  Go through each and act if they
            are new.
        */
        targetNodes.each ( function () {
            var jThis        = jQ(this);
            var alreadyFound = jThis.data ('alreadyFound')  ||  false;

            if (!alreadyFound) {
                //--- Call the payload function.
                var cancelFound     = actionFunction (jThis);
                if (cancelFound)
                    btargetsFound   = false;
                else
                    jThis.data ('alreadyFound', true);
            }
        } );
    }
    else {
        btargetsFound   = false;
    }

    //--- Get the timer-control variable for this selector.
    var controlObj      = waitForKeyElements.controlObj  ||  {};
    var controlKey      = selectorTxt.replace (/[^\w]/g, "_");
    var timeControl     = controlObj [controlKey];

    //--- Now set or clear the timer as appropriate.
    if (btargetsFound  &&  bWaitOnce  &&  timeControl) {
        //--- The only condition where we need to clear the timer.
        clearInterval (timeControl);
        delete controlObj [controlKey]
    }
    else {
        //--- Set a timer, if needed.
        if ( ! timeControl) {
            timeControl = setInterval ( function () {
                    waitForKeyElements (    selectorTxt,
                                            actionFunction,
                                            bWaitOnce,
                                            iframeSelector
                                        );
                },
                300
            );
            controlObj [controlKey] = timeControl;
        }
    }
    waitForKeyElements.controlObj   = controlObj;
}


text:
// ==UserScript==
// @name         betterUpstox
// @namespace    https://github.com/amit0rana/betterUpstox
// @version      0.02
// @description  Introduces small features on top of pro.upstox app
// @author       Amit
// @match        https://pro.upstox.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// @downloadURL  https://github.com/amit0rana/betterOptionsTrading/raw/master/betterUpstox.user.js
// @updateURL    https://github.com/amit0rana/betterOptionsTrading/raw/master/betterUpstox.meta.js
// ==/UserScript==


text:
// ==UserScript==
// @name         betterNSE
// @namespace    https://github.com/amit0rana/betterNSE
// @version      0.02
// @description  Introduces small features on top of nse website
// @author       Amit
// @match        https://www.nseindia.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// @downloadURL  https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.user.js
// @updateURL    https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.meta.js
// ==/UserScript==


text:
# betterUpstox

June 4 21
I have stopped trading on Upstox so this script is not updated. I am told it doesn't work anymore.

This userscript adds simple features on https://pro.upstox.com/trading UI

* Problem: We have several positions open at one time and it gets difficult to only focus on position belonging to specific strategy.
* Solution: Create a 'watchlist', give it name of your strategy, add scripts part of the strategy in the watchlist and click on 'Filter' to only see the positions pertaining to that strategy.
    * Once you filter, you can see number of positions in that strategy and total P&L

![usage](https://dl.dropbox.com/s/18ik0c8vffg1t4x/betterUpstoxUsageGif.gif?dl=0)
(if you can't see the image above, click here: https://dl.dropbox.com/s/18ik0c8vffg1t4x/betterUpstoxUsageGif.gif?dl=0)

# Installation

Follow below mentioned steps
* Install [Tempermonkey](https://www.tampermonkey.net/) for your browser (works on all browsers. Tested on Chrome, Vivaldi, Edge). [Chrome extension link](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo)
* Open [this link](https://github.com/amit0rana/betterOptionsTrading/raw/master/betterUpstox.user.js) in a new tab. Or copy paste <https://github.com/amit0rana/betterOptionsTrading/raw/master/betterUpstox.user.js>
* Tampermonkey will automatically identify the file as userscript and give you option to install.
* Click on 'Install' button.
* You can verify the installation by clicking on Tampermonkey icon (next to address bar) and then go to 'Dashboard'. You should see 'betterUpstox' installed.
* Now just go to <https://pro.upstox.com> (if the page is already open, then refresh it) and start using.

![filter](https://dl.dropbox.com/s/yt8wc31kcqxjw6w/betterUpstoxFilter.png?dl=0)
(if you can't see the image above click here: https://dl.dropbox.com/s/yt8wc31kcqxjw6w/betterUpstoxFilter.png?dl=0)

# Note
* Steps to disable the script

![disable](https://dl.dropbox.com/s/pp7bqiyzwak6cjp/troubleshootDisableBetterUpstoxVideo.gif?dl=0)
(if you can't see the image above click here: https://dl.dropbox.com/s/pp7bqiyzwak6cjp/troubleshootDisableBetterUpstoxVideo.gif?dl=0)

* How to upgrade to next version.

![upgrade](https://dl.dropbox.com/s/8urhzpu6x78yxhk/upgradeScriptBetterUpstox.png?dl=0)
(if you can't see the image above click here: https://dl.dropbox.com/s/8urhzpu6x78yxhk/upgradeScriptBetterUpstox.png?dl=0)
* Do not forget to refresh the page after update

* P&L doesn't update on realtime. It shows the P&L at the time of filter.


text:
# betterSensibull

This userscript adds simple features on strategy builder / payout chart screen.

Several times a strategy has positions across expiries. This script introduces a small dropdown to filter and see positions belonging to a specific expiry only.

![usage](https://dl.dropbox.com/s/snhgpe89gfbhyfe/sensibullExpirySelection.png?dl=0)


text:
Please look at all the images below to understand the process.

* Please note, the plugin doesn't automatically execute any order, so its safe.
* Button location has changed but I haven't updated the screenshots
* Plugin will just create a basket for you. You can edit/review the basket and then place order.
* Plugin does not read any confidential information. Basket is made and executed on zerodha website.
* Anytime to reset or cancel already copied orders, simply click on 'reset orders'.

Steps:
* Open both accounts in different windows. In my example, I am copying order from left window to right window.
![1. OpenBothAccounts](https://dl.dropbox.com/s/smt2ipq4k8otbke/1.%20OpenBothAccounts.png?dl=0)

* Goto order screen and click on 'executed orders'. Make sure you click on 'executed order' on both the winows. You should be seeing 'Send Order' button on top before proceeding. In order to copy orders its assumed that you have already executed orders in one account.
![2. ClickOnExecuteOrder](https://dl.dropbox.com/s/o239lgh97wz4bgr/2.%20ClickOnExecuteOrder.png?dl=0)

* Click on order info, as shown in the image.
![3. ClickOnOrderInfo](https://dl.dropbox.com/s/qk4hc2ubp37939i/3.%20ClickOnOrderInfo.png?dl=0)

* On the order info screen, click on 'Copy Order'. The number next to the button shows the number of orders already copied. You can follow this process and copy as many orders you want.
![4. ClickCopyOrder](https://dl.dropbox.com/s/j4jh0gjgccdjzs2/4.%20ClickCopyOrder.png?dl=0)

* You can verify number of orders by looking at count.
![5. VerifyNumberOfCopiedOrders](https://dl.dropbox.com/s/8pvywhn8b2gxxc3/5.%20VerifyNumberOfCopiedOrders.png?dl=0)

* Now go to second window and click on 'send order' button. This is a safe action, doesn't execute order. Just creates basket and shows to you.
![6. SecondWindowSendOrder](https://dl.dropbox.com/s/2gvggzyeryknlld/6.%20SecondWindowSendOrder.png?dl=0)

* Review the baskt, edit if needed and place order.
![7. ConfirmBasketEditIfNeeded](https://dl.dropbox.com/s/cnlde8u2ljdaqs4/7.%20ConfirmBasketEditIfNeeded.png?dl=0)


text:
// ==UserScript==
// @name         betterSharekhan
// @namespace    https://github.com/amit0rana/betterSharekhan
// @version      0.07
// @description  Introduces small features on top of newtrade.sharekhan.com
// @author       Amit
// @match        https://newtrade.sharekhan.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @require      https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js
// @require      https://raw.githubusercontent.com/kawanet/qs-lite/master/dist/qs-lite.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js
// @downloadURL  https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.user.js
// @updateURL    https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.meta.js
// ==/UserScript==


text:
// ==UserScript==
// @name         betterStocksDeveloper
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Small improvemnts to the betterStocks website
// @author       Amit
// @match        https://web.stocksdeveloper.in/*
// @icon         https://www.google.com/s2/favicons?domain=stocksdeveloper.in
// @grant        none
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// ==/UserScript==


text:
// ==UserScript==
// @name         betterNSE
// @namespace    https://github.com/amit0rana/betterNSE
// @version      0.02
// @description  Introduces small features on top of nse website
// @author       Amit
// @match        https://www.nseindia.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// @downloadURL  https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.user.js
// @updateURL    https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.meta.js
// ==/UserScript==

const formatter = Intl.NumberFormat('en-IN');
var isMultiply = true;

function changeButtonName() {
    var btn = $('#addLot');

    if (isMultiply) {
        $(btn).val('Divide 75');
        isMultiply = false;
    } else {
        isMultiply = true;
        $(btn).val('Multiply 75');
    }
}

function getNumber(n) {
    if (isMultiply) {
        return formatter.format(n*75);
    } else {
        return formatter.format(n/75);
    }
}

var i = document.createElement("INPUT");
i.type = 'button';
i.name='addLot';
i.value='Multiply 75';
i.classList.add("randomClassToHelpHide");
i.id='addLot';

function main() {
    $('#main_navbar').append(i);

    $(document).on('click',"#addLot",  function() {
        var allRows = $('#optionChainTable-indices > tbody > tr');
        allRows.each(function(rowIndex) {
            //2 , 3, 21, 22
            var col = $(this).find("td:nth-child(2)");
            var txt = parseInt($(col).text().split(",").join(""));

            if (!isNaN(txt)) {
                $(col).text(getNumber(txt));
            }

            col = $(this).find("td:nth-child(3)");
            txt = parseInt($(col).text().split(",").join(""));

            if (!isNaN(txt)) {
                $(col).text(getNumber(txt));
            }
            col = $(this).find("td:nth-child(21)");
            txt = parseInt($(col).text().split(",").join(""));

            if (!isNaN(txt)) {
                $(col).text(getNumber(txt));
            }
            col = $(this).find("td:nth-child(22)");
            txt = parseInt($(col).text().split(",").join(""));

            if (!isNaN(txt)) {
                $(col).text(getNumber(txt));
            }
        });
        changeButtonName();
    });

    $(document).on('change',"#expirySelect",  function() {
        isMultiply = false;
        changeButtonName();
    });
    $(document).on('click',".fullViewBtn",  function() {
        $(".randomClassToHelpHide").remove();
        $('#goBackSite').before(i);
    });
    $(document).on('click',"#goBackSite",  function() {
        $(".randomClassToHelpHide").remove();
        $('#main_navbar').append(i);
    });
}

main();


text:
------
Contributors:
- Amit
- JP

------

![1. Using NSE](https://dl.dropbox.com/s/jbtpnr0545dtpj1/using%20nse%20userscript.mov?dl=0)


text:
// ==UserScript==
// @name         betterUpstox
// @namespace    https://github.com/amit0rana/betterUpstox
// @version      0.02
// @description  Introduces small features on top of pro.upstox app
// @author       Amit
// @match        https://pro.upstox.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require      https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// @downloadURL  https://github.com/amit0rana/betterKite/raw/master/betterUpstox.user.js
// @updateURL    https://github.com/amit0rana/betterKite/raw/master/betterUpstox.meta.js
// ==/UserScript==

//window.jQ=jQuery.noConflict(true);

const D_LEVEL = D_LEVEL_INFO;

waitForKeyElements ("div._headerLinks_12987", main);
//main();

function init() {
    debug('initialising');
    var toggleLink = document.createElement("a");
    toggleLink.classList.add("randomClassToDelete");
    toggleLink.classList.add("_link_12987");
    toggleLink.id = "toggleLinkId";

    toggleLink.innerText = "Filter";
    var tag = $("#root > div > div > div._header_12987 > div._headerLinks_12987");
    debug(tag);

    tag.append(toggleLink);

    var spanForCount = document.createElement("span");
    spanForCount.classList.add("randomClassToDelete");
    spanForCount.classList.add("_link_12987");
    spanForCount.id = "positionText";
    //spanForCount.style="margin: 15px 0;margin-top: 15px;margin-right: 0px;margin-bottom: 15px;margin-left: 0px;border-right: 1px solid #e0e0e0;border-right-width: 1px;border-right-style: solid;border-right-color: rgb(224, 224, 224);padding: 0 10px;"

    tag.append(spanForCount);
}

function toggleFilter() {
    if (filterActive) {
        filterActive = false;
        $("#toggleLinkId").text("Filter");
    } else {
        filterActive = true;
        $("#toggleLinkId").text("Reset");
    }
}

var filterActive = false;

function main() {
    init();

    $(document).on('click', "#toggleLinkId", function () {
        debug('click');
        $(".allHiddenRows").show();
        if (filterActive) {
            debug('deactivating filter');
            toggleFilter();
            //jQ(".randomClassToDelete").remove();
            $("#positionText").text("");
        } else {
            debug('activating filter');
            var watchlistRowsDom = $("#watchlistTestId > div > div");
            debug("number " + watchlistRowsDom.length);
            if (watchlistRowsDom.length > 0) {
                var watchlistArray = [];
                watchlistRowsDom.each(function(rowIndex) {
                    var name = $(this).find("div._name_6136e").text();
                    if (name != "") {
                        var symbol = $(this).find("div._symbol_6136e").text()

                        watchlistArray.push(name+symbol);
                        debug(name + " " + symbol);
                    }

                });

                var pnl = 0;
                var positionRowsDom = $("#books > div > div > div > div > div > div > div > table > tbody > tr");
                debug("number " + positionRowsDom.length);
                if (positionRowsDom.length > 0) {
                    positionRowsDom.addClass("allHiddenRows");
                    var numberOfPositions = 0;
                    positionRowsDom.each(function(rowIndex) {
                        var name = $(this).find("div._symbolName_2f3f1").text();
                        if (name != "") {
                            var symbol = $(this).find("div._category_2f3f1").text()

                            var workingItem = name+symbol;
                            if (watchlistArray.includes(workingItem)) {
                                //all good show.
                                var p = $(this).find("[data-id=booksOverallPnL]").text().split(",").join("");
                                pnl += parseFloat(p);
                                debug(p);
                                numberOfPositions++;
                                debug(workingItem);
                            } else {
                                $(this).hide();
                            }
                        }

                    });
                    if (numberOfPositions > 0 ) {
                        $("#positionText").text("("+numberOfPositions+") " + formatter.format(pnl));
                    }
                }

            }
            toggleFilter();
        }
    });

}


text:
// ==UserScript==
// @name         betterTradingeconomics
// @namespace    https://github.com/amit0rana/betterTradingeconomics
// @version      0.03
// @description  Introduces small features on top of betterTradingeconomics site
// @author       Amit
// @match        https://tradingeconomics.com/*
// @match        https://in.investing.com/*
// @grant        GM_addStyle
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
// @require      https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js
// @require      https://gist.github.com/raw/2625891/waitForKeyElements.js
// @downloadURL  https://github.com/amit0rana/betterOptionsTrading/raw/master/betterTradingeconomics.user.js
// @updateURL    https://github.com/amit0rana/betterOptionsTrading/raw/master/betterTradingeconomics.meta.js
// ==/UserScript==