├── .gitignore ├── LICENSE ├── README.md ├── at-desktop-2.6.0.jar ├── betterCommon.js ├── betterKite.md ├── betterKite.meta.js ├── betterKite.user.js ├── betterKiteCopyOrders.md ├── betterNSE.md ├── betterNSE.meta.js ├── betterNSE.user.js ├── betterOipulse.user.js ├── betterSensibull.md ├── betterSensibull.meta.js ├── betterSensibull.user.js ├── betterSharekhan.meta.js ├── betterSharekhan.user.js ├── betterStocksDeveloper.meta.js ├── betterStocksDeveloper.user.js ├── betterTradingeconomics.meta.js ├── betterTradingeconomics.user.js ├── betterUpstox.md ├── betterUpstox.meta.js ├── betterUpstox.user.js ├── minimalGA.js └── minimalGA.min.js /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/settings.json 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Disclaimer: I have made this tool for my personal use, it may have bugs. 2 | 3 | This repository has 2 userscripts 4 | * [For Zerodha](https://github.com/amit0rana/betterOptionsTrading/blob/master/betterKite.md) 5 | * [For Upstox](https://github.com/amit0rana/betterOptionsTrading/blob/master/betterUpstox.md) 6 | * [For Sensibull](https://github.com/amit0rana/betterOptionsTrading/blob/master/betterSensibull.md) 7 | * [For NSEIndia site] (https://github.com/amit0rana/betterOptionsTrading/blob/master/betterNSE.md) -------------------------------------------------------------------------------- /at-desktop-2.6.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/1a059badd707a4176613dcb3fcf50f62b781b55a/at-desktop-2.6.0.jar -------------------------------------------------------------------------------- /betterCommon.js: -------------------------------------------------------------------------------- 1 | // This is free and unencumbered software released into the public domain. 2 | 3 | // Anyone is free to copy, modify, publish, use, compile, sell, or 4 | // distribute this software, either in source code form or as a compiled 5 | // binary, for any purpose, commercial or non-commercial, and by any 6 | // means. 7 | 8 | // In jurisdictions that recognize copyright laws, the author or authors 9 | // of this software dedicate any and all copyright interest in the 10 | // software to the public domain. We make this dedication for the benefit 11 | // of the public at large and to the detriment of our heirs and 12 | // successors. We intend this dedication to be an overt act of 13 | // relinquishment in perpetuity of all present and future rights to this 14 | // software under copyright law. 15 | 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | // OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | // For more information, please refer to 25 | 26 | const D_LEVEL_INFO = 2; 27 | const D_LEVEL_DEBUG = 1; 28 | const D_LEVEL_NONE = 100; 29 | 30 | const log = function (level, logInfo) { 31 | switch (level) { 32 | case D_LEVEL_DEBUG: 33 | console.debug(logInfo); 34 | break; 35 | case D_LEVEL_INFO: 36 | console.info(logInfo); 37 | break; 38 | default: 39 | console.log(logInfo); 40 | } 41 | } 42 | const debug = function (logInfo) { 43 | if (D_LEVEL <= D_LEVEL_DEBUG) { 44 | log(D_LEVEL_DEBUG, logInfo); 45 | } 46 | } 47 | const info = function (logInfo) { 48 | if (D_LEVEL <= D_LEVEL_INFO) { 49 | log(D_LEVEL_INFO, logInfo); 50 | } 51 | } 52 | const formatter = Intl.NumberFormat('en-IN', { 53 | style: 'currency', currency: 'INR' 54 | }); 55 | 56 | const reloadPage = function (values) { 57 | window.location.reload(); 58 | } 59 | 60 | const getCookie = function (name) { 61 | const value = `; ${document.cookie}`; 62 | const parts = value.split(`; ${name}=`); 63 | if (parts.length === 2) return parts.pop().split(';').shift(); 64 | } 65 | 66 | const getFunctionName = function () { 67 | // var me = arguments.callee.toString(); 68 | // me = me.substr('function '.length); 69 | // me = me.substr(0, me.indexOf('(')); 70 | return getFunctionName.caller.name; 71 | } -------------------------------------------------------------------------------- /betterKite.md: -------------------------------------------------------------------------------- 1 | # betterKite 2 | 3 | This is free and unencumbered software released into the public domain. 4 | 5 | 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. 6 | 7 | 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. 8 | 9 | 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. 10 | 11 | For more information, please refer to [](https://unlicense.org) 12 | 13 | Very simple "userscript" which adds several features to kite.zerodha.com ui (screenshots below) 14 | * Several features to make options trading easy like SmartLimit, Split orders, bump orders etc 15 | * See P&L and current value 'including' pledged stocks 16 | * Automatically create SL order, then 'trail' and 'save profit' by just one click. 17 | * 'Smart Limit' Place LIMIT orders at best offer/bid price. 18 | * See quantities in lots 19 | * See real profit based on currently holding quantity 20 | * See possible Nifty monthly and weekly range based on INDIA VIX 21 | * See realised p&l for the day 22 | * Avoid quantity freeze limitation, breaks bigger order into multiple orders 23 | * Group positions under strategies 24 | * For a strategy, check how much margin can be freed by taking hedge buy positions 25 | * See P&L for each strategy 26 | * See margin utilised for each position or for a strategy 27 | * Mark trades as reference trade or martingale 28 | * See your ROI 29 | * Get explanation of various rows in funds page 30 | * Filter trades by expiry in sensibull pop-up 31 | * Auto group strategy by scrip and expiry 32 | * Auto filter Watch list for scrip in positions 33 | * Auto add ATM +- n strike options in the watchlist based on expiry 34 | * Highlight position when you click on scrip in Watch list 35 | * Filter mis only, pe online or CE only positions 36 | * For a strategy, see potentially how much margin can be released by buying hedge positions 37 | ![savemargin](https://dl.dropbox.com/s/aiacmoefjxdb35l/saveMargin.png?dl=0) 38 | * 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. 39 | * Check this image on enabling CORS: 40 | ![corsusage](https://dl.dropbox.com/s/mbktrw9dkqu4wl0/corsToggle.png?dl=0) 41 | * Use Full browser width 42 | * Clear watchlist 43 | 44 | ------ 45 | Contributors 46 | - Amit 47 | - Yuva (@rbcdev) 48 | 49 | ------ 50 | 51 | # Installation 52 | 53 | Follow below mentioned steps 54 | * 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) 55 | * Open [this link](https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.user.js) in a new tab. Or copy paste 56 | * Tampermonkey will automatically identify the file as userscript and give you option to install. (sell image below) 57 | * ![tampermonkey install](https://dl.dropbox.com/s/khs3itzctu13ayw/tampermonkeyInstall.png?dl=0) 58 | * Click on 'Install' button. 59 | * 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) 60 | * ![dashboard menu](https://dl.dropbox.com/s/dv1reqb84mz00bm/dashboardmenuoption.png?dl=0) 61 | * ![dashboard sample](https://dl.dropbox.com/s/blv2j9t8e6iohkt/dashboardSample.png?dl=0) 62 | * Now just go to (refresh the page if its already open) and start using. 63 | * 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. 64 | * Below plugin is required only if you want to use Margin Calculator methods* Install CORS plugin for your browser. 65 | * Firefox: https://addons.mozilla.org/en-US/firefox/addon/access-control-allow-origin/ 66 | * Edge: https://microsoftedge.microsoft.com/addons/detail/allow-cors-accesscontro/bhjepjpgngghppolkjdhckmnfphffdag?hl=en-GB 67 | * chrome: https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf 68 | 69 | ------ 70 | # https://kite.zerodha.com/positions page 71 | * 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'. 72 | ![OrderScreen](https://dl.dropbox.com/s/x9kaheg26hdsuc0/sl_create_sl_order.png?dl=0) 73 | ![pendingORder](https://dl.dropbox.com/s/11vrrzch6bi9ysd/sl_trail_save_profit_btn.png?dl=0) 74 | ![settings](https://dl.dropbox.com/s/sbuqqclaerljpva/sl_order_settings.png?dl=0) 75 | ![help1](https://dl.dropbox.com/s/u83bs5ec9gdl2t8/sl_save_profit_help.png?dl=0) 76 | ![help2](https://dl.dropbox.com/s/ot3rjkhnvid0fee/sl_trail_help.png?dl=0) 77 | * '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. 78 | ![SmartLimit](https://dl.dropbox.com/s/b02kq0vjxscw3p2/smartLimit.png?dl=0) 79 | * 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 80 | ![QuantityFeeze](https://dl.dropbox.com/s/h55i2gkk6gj5y6j/qtyFreeze.png?dl=0) 81 | ![QuantityFeeze1](https://dl.dropbox.com/s/n4jd45bpzl6dh1p/qtySetings.png?dl=0) 82 | ![QuantityFeeze2](https://dl.dropbox.com/s/sc4xd7wsofz9zdp/qtySettings-2.png?dl=0) 83 | * Click on quanity to see it in lots, click again to remove it ![lots](https://dl.dropbox.com/s/9p7sxvuu2kguqly/seeQuantityInLots.png?dl=0) 84 | * Click on any of the pnl to enable/disable this feature ![pnl](https://dl.dropbox.com/s/7oql0zgzihvjsw4/seeRealProfit.png?dl=0) 85 | * Step 1: Add Nifty 50 to Pin 1, click on Nifty value to see monthly range, click on change value to see weekly range 86 | ![Monthly](https://dl.dropbox.com/s/vr1x6b8llju46ng/monthlyRangeVix.png?dl=0) 87 | ![Weekly](https://dl.dropbox.com/s/l28p58dc2dr2w4u/WeeklyRangeVix.png?dl=0) 88 | * Click on Total P&L to start seeting 'Realised P&L' ![realised](https://dl.dropbox.com/s/b6q7b6zn5jimkvd/realisedPnlForTheDay.png?dl=0) 89 | * Strategies are grouped in 3 ways. 90 | * (1) Strategies are auto grouped by script name. So all INFY strategies will be auto grouped under 'INFY' 91 | * (2) Strategies are auto grouped by expiry. 92 | * (3) Manual / custom strategies. 93 | * Once you select a strategy from the dropdown, only relevant positions will be shown. 94 | ![strategies](https://dl.dropbox.com/s/414mh3oqvx4ppf2/strategies.png?dl=0) 95 | 96 | * ![strategies2](https://dl.dropbox.com/s/qjyok361dk9jo6c/strategies2.png?dl=0) 97 | * You can also see strategy's P&L on the right side of dropdown. 98 | * You can see MARGIN required for the 'group of positions' along with P&L on top. 99 | * You can also see margin required for individual position at the bottom of the table. 100 | * ![marginUsage](https://dl.dropbox.com/s/8cii9hr27ctqebc/marginCalculationUsage.png?dl=0) 101 | * Tag your reference trades and martingales for easier identification. You can give custom name and color for easy identification 102 | ![referenceTags](https://dl.dropbox.com/s/i18bklcdebtagia/referenceTags.png?dl=0) 103 | * Quickly see total P&L of 'selected' positions. 104 | ![addPositions](https://dl.dropbox.com/s/mvavj8njmt2xvtp/pnlAddition.png?dl=0) 105 | * "Filter" button is added next to 1,2,3,4,5 watchlist. You can use this to filter your watchlist. 106 | ![watchlistFilter](https://dl.dropbox.com/s/5gf2paw5pk9you6/watchlistFilter.png?dl=0) 107 | * Filter positions within the sensibull interface. 108 | ![zerodaSensibullAnalyze](https://dl.dropbox.com/s/rqzpo214j961x1u/zerodaSensibullAnalyze.png?dl=0) 109 | ![zerodhaSensibullFilters](https://dl.dropbox.com/s/1s91foyuaewkfed/zerodhaSensibullFilters.png?dl=0) 110 | 111 | ------ 112 | # https://kite.zerodha.com/holdings page 113 | * See P&L and current value 'including' pledged stocks 114 | ![pledged](https://dl.dropbox.com/s/mnhx9efdjpo5d2m/pledgedPnl.png?dl=0) 115 | * Group your holdings in 'categories' or 'tags' 116 | * Small tag is shown next to your stock name indicating which category it belongs to. 117 | ![tags](https://dl.dropbox.com/s/ygk9id8c21b3mi8/HoldingsWithTags.png?dl=0) 118 | * Filter stocks based on category. Once filter is applied only stocks in that category will be shown in 'watchlist' or 'orders' or 'holdings' screen 119 | ![header](https://dl.dropbox.com/s/zvefkb2pis0ygq4/headerWithTagSelector.png?dl=0) 120 | * One stock can be in multiple categories. 121 | * 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. 122 | 123 | ------ 124 | # [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) 125 | 126 | ------ 127 | # https://console.zerodha.com/reports/pnl page 128 | Introduces a convenence link to see "current month's" F&O P&L 129 | ![usage](https://dl.dropbox.com/s/vhrh6qqf775kg9y/foThisMonth.png?dl=0) 130 | 131 | ------ 132 | # https://kite.zerodha.com/funds page 133 | Introduced help text on each row explaining what different rows mean. 134 | ![usage](https://dl.dropbox.com/s/cjo8y462vxpp7iw/usageFundsHelpText.png?dl=0) 135 | 136 | ------ 137 | # Installation 138 | 139 | Follow below mentioned steps 140 | * 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) 141 | * Open [this link](https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.user.js) in a new tab. Or copy paste 142 | * Tampermonkey will automatically identify the file as userscript and give you option to install. (sell image below) 143 | * ![tampermonkey install](https://dl.dropbox.com/s/khs3itzctu13ayw/tampermonkeyInstall.png?dl=0) 144 | * Click on 'Install' button. 145 | * 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) 146 | * ![dashboard menu](https://dl.dropbox.com/s/dv1reqb84mz00bm/dashboardmenuoption.png?dl=0) 147 | * ![dashboard sample](https://dl.dropbox.com/s/blv2j9t8e6iohkt/dashboardSample.png?dl=0) 148 | * Now just go to (refresh the page if its already open) and start using. 149 | * 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. 150 | 151 | ------ 152 | # How to use 153 | * Once installation is done, go to 'holdings' page and you will see a dropdown next to the logo listing all your groups. 154 | * On holding pages, dropdown will show all the groups under which you want to keep your holding stocks 155 | * On Positions page, dropdown will show all the strategies. 156 | * you can now filter 'positions' screen and 'holdings' screen based on your selection. Watchlist will also be filtered. 157 | * When you click on watchlist row, if same stock is present in your holdings list, it will be highlighted and brought to focus. 158 | * You can enabled/disable the filter dropdown by clicking on 'Position()' or 'Holdings()' heading. 159 | 160 | ------ 161 | # Steps for grouping your Holdings 162 | * Refer to images below on how to use. 163 | * Adding tag : Go to holdings section, click on + icon next to the stock name to add tags. 164 | * Removing tag: Click on tag to remove it. 165 | * Filtering stocks: Use the dropdown for filtering. 166 | * ![how to use](https://dl.dropbox.com/s/tllta7nzcfl145a/holdingsHowToUse.gif?dl=0) 167 | * ![add remove tags](https://dl.dropbox.com/s/nbjuxiu7yh9p51i/addingRemovingTags.gif?dl=0) 168 | 169 | ------ 170 | # Steps for marking trades as reference trades or base trade or martingales 171 | * 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. 172 | * Tag should be formated as tagName.color for example: RT.red or BS.blue etc. 173 | 174 | ------ 175 | # Steps for custom strategies. 176 | * Please see the animated gif below 177 | ![addingDeletingStrategies](https://dl.dropbox.com/s/23mec9h3zd8iolw/AddingDeletingStrategy.gif?dl=0) 178 | (if you can't see the image above see this video: https://dl.dropbox.com/s/iay4ld2ibdx3t91/AddingDeletingStrategy.mp4?dl=0) 179 | 180 | ------ 181 | TODOs 182 | * Instructions for [Violentmonkey](https://openuserjs.org/about/Violentmonkey-for-Chrome) 183 | * Filtering on order page 184 | * Let users disable analytics 185 | 186 | Note 187 | This script tracks following events for analytics. No data is passed/stored/tracked. This only tells that action was taken. 188 | * Script loaded 189 | * Script enabled/disabled 190 | * Watchlist filter used 191 | * Tag added 192 | * Tag deleted 193 | * Filter applied 194 | 195 | [//]: # (SEBI MANDATORY DISCLAIMER AS REQUIRED BY SEBI) 196 | 197 | [//]: # (KIND ATTENTION TO ALL USERS) 198 | 199 | [//]: # (Disclaimer from Developers as per SEBI norms: Equity Investment are subject to 100%) 200 | [//]: # (market risks. Refer your financial consultant advice before Investing. This code is) 201 | [//]: # (only for Educational and Learning, Knowledge Purposes. Developers have no) 202 | [//]: # (responsibility for your intended decision & financial losses. Keep calculated & always) 203 | [//]: # (analyzed your cash osition and risk bearing capacity before following any advise.) 204 | [//]: # (Stock market investments and using this script are VERY RISKY and using ths code, you ) 205 | [//]: # (agree that you understand the Market risks involved. Profits and Losses are part of Share market.) 206 | [//]: # (Kindly understand and act wisely.) 207 | 208 | [//]: # (Disclaimer/ disclosure) 209 | 210 | [//]: # (👉This script does not provide any tips/recommendations/advice) 211 | [//]: # (👉All features are only for education and learning purpose.) 212 | [//]: # (👉Do Consult your financial advisor before taking trades or investment decisions) 213 | [//]: # (👉Developers are not responsible for any financial losses) 214 | [//]: # (👉Disclaimer/disclosure/terms and conditions applicable to all users of this script) 215 | 216 | [//]: # (All the features are for education and learning purpose only👍) -------------------------------------------------------------------------------- /betterKite.meta.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterKite 3 | // @namespace https://github.com/amit0rana/betterKite 4 | // @version 5.07 5 | // @description Introduces small features on top of kite app 6 | // @author Amit with inputs from bsvinay, sidonkar, rbcdev 7 | // @match https://kite.zerodha.com/* 8 | // @match https://console.zerodha.com/* 9 | // @match https://insights.sensibull.com/* 10 | // @grant GM_setValue 11 | // @grant GM_getValue 12 | // @grant GM_addStyle 13 | // @grant GM_getResourceText 14 | // @grant GM_registerMenuCommand 15 | // @require https://raw.githubusercontent.com/sizzlemctwizzle/GM_config/refs/heads/master/gm_config.js 16 | // @require https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js 17 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 18 | // @require https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js 19 | // @require https://raw.githubusercontent.com/kawanet/qs-lite/master/dist/qs-lite.min.js 20 | // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js 21 | // @require https://unpkg.com/@popperjs/core@2 22 | // @require https://unpkg.com/tippy.js@6 23 | // @require https://cdn.jsdelivr.net/npm/sweetalert2@11 24 | // @require https://cdn.jsdelivr.net/npm/toastify-js 25 | // @require https://raw.githubusercontent.com/emn178/js-sha256/refs/heads/master/src/sha256.js 26 | // @resource TOASTIFY_CSS https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css 27 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.user.js 28 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterKite.meta.js 29 | // ==/UserScript== 30 | 31 | // This is free and unencumbered software released into the public domain. 32 | 33 | // Anyone is free to copy, modify, publish, use, compile, sell, or 34 | // distribute this software, either in source code form or as a compiled 35 | // binary, for any purpose, commercial or non-commercial, and by any 36 | // means. 37 | 38 | // In jurisdictions that recognize copyright laws, the author or authors 39 | // of this software dedicate any and all copyright interest in the 40 | // software to the public domain. We make this dedication for the benefit 41 | // of the public at large and to the detriment of our heirs and 42 | // successors. We intend this dedication to be an overt act of 43 | // relinquishment in perpetuity of all present and future rights to this 44 | // software under copyright law. 45 | 46 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 47 | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 48 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 49 | // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 50 | // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 51 | // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 52 | // OTHER DEALINGS IN THE SOFTWARE. 53 | 54 | // For more information, please refer to 55 | 56 | // SEBI MANDATORY DISCLAIMER AS REQUIRED BY SEBI 57 | 58 | // KIND ATTENTION TO ALL USERS 59 | 60 | // Disclaimer from Developers as per SEBI norms: Equity Investment are subject to 100% 61 | // market risks. Refer your financial consultant advice before Investing. This code is 62 | // only for Educational and Learning, Knowledge Purposes. Developers have no 63 | // responsibility for your intended decision & financial losses. Keep calculated & always 64 | // analyzed your cash osition and risk bearing capacity before following any advise. 65 | // Stock market investments and using this script are VERY RISKY and using ths code, you 66 | // agree that you understand the Market risks involved. Profits and Losses are part of Share market. 67 | // Kindly understand and act wisely. 68 | 69 | // Disclaimer/ disclosure 70 | 71 | // 👉This script does not provide any tips/recommendations/advice 72 | // 👉All features are only for education and learning purpose. 73 | // 👉Do Consult your financial advisor before taking trades or investment decisions 74 | // 👉Developers are not responsible for any financial losses 75 | // 👉Disclaimer/disclosure/terms and conditions applicable to all users of this script 76 | 77 | // All the features are for education and learning purpose only👍 -------------------------------------------------------------------------------- /betterKiteCopyOrders.md: -------------------------------------------------------------------------------- 1 | Please look at all the images below to understand the process. 2 | 3 | * Please note, the plugin doesn't automatically execute any order, so its safe. 4 | * Button location has changed but I haven't updated the screenshots 5 | * Plugin will just create a basket for you. You can edit/review the basket and then place order. 6 | * Plugin does not read any confidential information. Basket is made and executed on zerodha website. 7 | * Anytime to reset or cancel already copied orders, simply click on 'reset orders'. 8 | 9 | Steps: 10 | * Open both accounts in different windows. In my example, I am copying order from left window to right window. 11 | ![1. OpenBothAccounts](https://dl.dropbox.com/s/smt2ipq4k8otbke/1.%20OpenBothAccounts.png?dl=0) 12 | 13 | 14 | * 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. 15 | ![2. ClickOnExecuteOrder](https://dl.dropbox.com/s/o239lgh97wz4bgr/2.%20ClickOnExecuteOrder.png?dl=0) 16 | 17 | * Click on order info, as shown in the image. 18 | ![3. ClickOnOrderInfo](https://dl.dropbox.com/s/qk4hc2ubp37939i/3.%20ClickOnOrderInfo.png?dl=0) 19 | 20 | * 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. 21 | ![4. ClickCopyOrder](https://dl.dropbox.com/s/j4jh0gjgccdjzs2/4.%20ClickCopyOrder.png?dl=0) 22 | 23 | * You can verify number of orders by looking at count. 24 | ![5. VerifyNumberOfCopiedOrders](https://dl.dropbox.com/s/8pvywhn8b2gxxc3/5.%20VerifyNumberOfCopiedOrders.png?dl=0) 25 | 26 | * 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. 27 | ![6. SecondWindowSendOrder](https://dl.dropbox.com/s/2gvggzyeryknlld/6.%20SecondWindowSendOrder.png?dl=0) 28 | 29 | * Review the baskt, edit if needed and place order. 30 | ![7. ConfirmBasketEditIfNeeded](https://dl.dropbox.com/s/cnlde8u2ljdaqs4/7.%20ConfirmBasketEditIfNeeded.png?dl=0) -------------------------------------------------------------------------------- /betterNSE.md: -------------------------------------------------------------------------------- 1 | ------ 2 | Contributors: 3 | - Amit 4 | - JP 5 | 6 | ------ 7 | 8 | ![1. Using NSE](https://dl.dropbox.com/s/jbtpnr0545dtpj1/using%20nse%20userscript.mov?dl=0) -------------------------------------------------------------------------------- /betterNSE.meta.js: -------------------------------------------------------------------------------- 1 | 2 | // ==UserScript== 3 | // @name betterNSE 4 | // @namespace https://github.com/amit0rana/betterNSE 5 | // @version 0.02 6 | // @description Introduces small features on top of nse website 7 | // @author Amit 8 | // @match https://www.nseindia.com/* 9 | // @grant GM_setValue 10 | // @grant GM_getValue 11 | // @grant GM_addStyle 12 | // @grant GM_registerMenuCommand 13 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 14 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 15 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 16 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.user.js 17 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.meta.js 18 | // ==/UserScript== 19 | -------------------------------------------------------------------------------- /betterNSE.user.js: -------------------------------------------------------------------------------- 1 | 2 | // ==UserScript== 3 | // @name betterNSE 4 | // @namespace https://github.com/amit0rana/betterNSE 5 | // @version 0.02 6 | // @description Introduces small features on top of nse website 7 | // @author Amit 8 | // @match https://www.nseindia.com/* 9 | // @grant GM_setValue 10 | // @grant GM_getValue 11 | // @grant GM_addStyle 12 | // @grant GM_registerMenuCommand 13 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 14 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 15 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 16 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.user.js 17 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterNSE.meta.js 18 | // ==/UserScript== 19 | 20 | const formatter = Intl.NumberFormat('en-IN'); 21 | var isMultiply = true; 22 | 23 | function changeButtonName() { 24 | var btn = $('#addLot'); 25 | 26 | if (isMultiply) { 27 | $(btn).val('Divide 75'); 28 | isMultiply = false; 29 | } else { 30 | isMultiply = true; 31 | $(btn).val('Multiply 75'); 32 | } 33 | } 34 | 35 | function getNumber(n) { 36 | if (isMultiply) { 37 | return formatter.format(n*75); 38 | } else { 39 | return formatter.format(n/75); 40 | } 41 | } 42 | 43 | var i = document.createElement("INPUT"); 44 | i.type = 'button'; 45 | i.name='addLot'; 46 | i.value='Multiply 75'; 47 | i.classList.add("randomClassToHelpHide"); 48 | i.id='addLot'; 49 | 50 | function main() { 51 | $('#main_navbar').append(i); 52 | 53 | $(document).on('click',"#addLot", function() { 54 | var allRows = $('#optionChainTable-indices > tbody > tr'); 55 | allRows.each(function(rowIndex) { 56 | //2 , 3, 21, 22 57 | var col = $(this).find("td:nth-child(2)"); 58 | var txt = parseInt($(col).text().split(",").join("")); 59 | 60 | if (!isNaN(txt)) { 61 | $(col).text(getNumber(txt)); 62 | } 63 | 64 | col = $(this).find("td:nth-child(3)"); 65 | txt = parseInt($(col).text().split(",").join("")); 66 | 67 | if (!isNaN(txt)) { 68 | $(col).text(getNumber(txt)); 69 | } 70 | col = $(this).find("td:nth-child(21)"); 71 | txt = parseInt($(col).text().split(",").join("")); 72 | 73 | if (!isNaN(txt)) { 74 | $(col).text(getNumber(txt)); 75 | } 76 | col = $(this).find("td:nth-child(22)"); 77 | txt = parseInt($(col).text().split(",").join("")); 78 | 79 | if (!isNaN(txt)) { 80 | $(col).text(getNumber(txt)); 81 | } 82 | }); 83 | changeButtonName(); 84 | }); 85 | 86 | $(document).on('change',"#expirySelect", function() { 87 | isMultiply = false; 88 | changeButtonName(); 89 | }); 90 | $(document).on('click',".fullViewBtn", function() { 91 | $(".randomClassToHelpHide").remove(); 92 | $('#goBackSite').before(i); 93 | }); 94 | $(document).on('click',"#goBackSite", function() { 95 | $(".randomClassToHelpHide").remove(); 96 | $('#main_navbar').append(i); 97 | }); 98 | } 99 | 100 | main(); -------------------------------------------------------------------------------- /betterOipulse.user.js: -------------------------------------------------------------------------------- 1 | 2 | // ==UserScript== 3 | // @name betterOipulse 4 | // @namespace https://github.com/amit0rana/betterOipulse 5 | // @version 0.01 6 | // @description 7 | // @author Amit 8 | // @match https://oipulse.com/* 9 | // @grant GM_setValue 10 | // @grant GM_getValue 11 | // @grant GM_addStyle 12 | // @grant GM_registerMenuCommand 13 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 14 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 15 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 16 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterOipulse.user.js 17 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterOipulse.meta.js 18 | // ==/UserScript== 19 | 20 | const formatter = Intl.NumberFormat('en-IN'); 21 | 22 | 23 | var i = document.createElement("INPUT"); 24 | i.type = 'button'; 25 | i.name='addLot'; 26 | i.value='Multiply 75'; 27 | i.classList.add("randomClassToHelpHide"); 28 | i.id='addLot'; 29 | 30 | function main() { 31 | $('#main_navbar').append(i); 32 | 33 | $(document).on('click',"#addLot", function() { 34 | var allRows = $('#optionChainTable-indices > tbody > tr'); 35 | allRows.each(function(rowIndex) { 36 | //2 , 3, 21, 22 37 | var col = $(this).find("td:nth-child(2)"); 38 | var txt = parseInt($(col).text().split(",").join("")); 39 | 40 | if (!isNaN(txt)) { 41 | $(col).text(getNumber(txt)); 42 | } 43 | 44 | col = $(this).find("td:nth-child(3)"); 45 | txt = parseInt($(col).text().split(",").join("")); 46 | 47 | if (!isNaN(txt)) { 48 | $(col).text(getNumber(txt)); 49 | } 50 | col = $(this).find("td:nth-child(21)"); 51 | txt = parseInt($(col).text().split(",").join("")); 52 | 53 | if (!isNaN(txt)) { 54 | $(col).text(getNumber(txt)); 55 | } 56 | col = $(this).find("td:nth-child(22)"); 57 | txt = parseInt($(col).text().split(",").join("")); 58 | 59 | if (!isNaN(txt)) { 60 | $(col).text(getNumber(txt)); 61 | } 62 | }); 63 | changeButtonName(); 64 | }); 65 | 66 | $(document).on('change',"#expirySelect", function() { 67 | isMultiply = false; 68 | changeButtonName(); 69 | }); 70 | $(document).on('click',".fullViewBtn", function() { 71 | $(".randomClassToHelpHide").remove(); 72 | $('#goBackSite').before(i); 73 | }); 74 | $(document).on('click',"#goBackSite", function() { 75 | $(".randomClassToHelpHide").remove(); 76 | $('#main_navbar').append(i); 77 | }); 78 | } 79 | 80 | main(); -------------------------------------------------------------------------------- /betterSensibull.md: -------------------------------------------------------------------------------- 1 | # betterSensibull 2 | 3 | This userscript adds simple features on strategy builder / payout chart screen. 4 | 5 | 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. 6 | 7 | ![usage](https://dl.dropbox.com/s/snhgpe89gfbhyfe/sensibullExpirySelection.png?dl=0) -------------------------------------------------------------------------------- /betterSensibull.meta.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterSensibull 3 | // @namespace https://github.com/amit0rana/betterSensibull 4 | // @version 0.14 5 | // @description Introduces small features on top of sesibull 6 | // @author Amit 7 | // @match https://web.sensibull.com/* 8 | // @grant GM_setValue 9 | // @grant GM_getValue 10 | // @grant GM_addStyle 11 | // @grant GM_registerMenuCommand 12 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 13 | // @require https://github.com/amit0rana/betterOptionsTrading/raw/master/common.js 14 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 15 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 16 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSensibull.user.js 17 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSensibull.meta.js 18 | // ==/UserScript== -------------------------------------------------------------------------------- /betterSensibull.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterSensibull 3 | // @namespace https://github.com/amit0rana/betterSensibull 4 | // @version 0.14 5 | // @description Introduces small features on top of sensibull 6 | // @author Amit 7 | // @match https://web.sensibull.com/* 8 | // @grant GM_setValue 9 | // @grant GM_getValue 10 | // @grant GM_addStyle 11 | // @grant GM_registerMenuCommand 12 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 13 | // @require https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js 14 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 15 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 16 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSensibull.user.js 17 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSensibull.meta.js 18 | // ==/UserScript== 19 | 20 | console.log("bs: script load"); 21 | 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 }; 22 | 23 | //window.jQ=jQuery.noConflict(true); 24 | const VERSION = "v0.14"; 25 | const PRO_MODE = false; 26 | 27 | const g_config = new MonkeyConfig({ 28 | title: 'betterSensibull Settings', 29 | menuCommand: true, 30 | onSave: reloadPage, 31 | params: { 32 | logging: { 33 | type: 'select', 34 | choices: ['Info', 'Debug'], 35 | values: [D_LEVEL_INFO, D_LEVEL_DEBUG], 36 | default: D_LEVEL_INFO 37 | } 38 | } 39 | }); 40 | const D_LEVEL = g_config.get('logging'); 41 | 42 | const SENSIBULL_DOM_PATHS = { 43 | domForPlacingToggleSelectBox: "button:contains('Clear Positions')", 44 | domForPositionsRows: "div.style__TradeTableRow-sc-szonbw-7", 45 | //domForPositionExpiry: 'div:nth-child(1) > div:nth-child(2) > div > div:nth-child(2)', 46 | domForPositionExpiry: 'div.tradesItemViewDate', 47 | // domForStrategySuggestions: '#app > div > div.page-sidebar-is-open.sn-page--builder.style__AppWrapper-djPJnZ.gvrWYn > div.sn-l__app-content.style__AppContent-haAgYm.korEfl > div.style__ContainerSpacing-kZpkBx.kJeLXd > div > div.style__BuilderWrapper-hHFjHn.nAQhs > div.style__BuilderColRight-jAAkJD.hbmLzB > div.style__BuilderPresetStrategiesWrapper-iJakRq.jDHsHZ', 48 | domForCheckbox: 'span > span:nth-child(1) > input' 49 | }; 50 | 51 | waitForKeyElements("button:contains('Add New Trade')", function () { 52 | console.log('onwait'); 53 | main(); 54 | }); 55 | $(document).on('click', "#app > div > div.style__AppWrapper-gfb86b-0.kMpPMs.page-sidebar-is-open.sn-page--builder > div.style__NavBarWrapper-gfb86b-4.Jdjhy.sn-l__navBar-wrapper > div > div:nth-child(2) > div > div:nth-child(1) > div", function () { 56 | console.log('onclick'); 57 | 58 | main(); 59 | }); 60 | 61 | 62 | 63 | // all behavior related actions go here. 64 | function main() { 65 | console.log("bs: main started"); 66 | // $(SENSIBULL_DOM_PATHS.domForStrategySuggestions).remove(); 67 | $("#toggleSelectboxID").remove(); 68 | 69 | var rows = $(SENSIBULL_DOM_PATHS.domForPositionsRows); 70 | console.log('bs: rows: ' + rows.length); 71 | 72 | var selectBox = document.createElement("SELECT"); 73 | selectBox.id = "toggleSelectboxID"; 74 | selectBox.classList.add("randomClassToHelpHide"); 75 | //selectBox.style="margin: 15px 0;margin-top: 15px;margin-right: 0px;margin-bottom: 15px;margin-left: 0px;background-color: var(--color-bg-default)" 76 | 77 | var option = document.createElement("option"); 78 | option.text = "Toggle"; 79 | option.value = "All"; 80 | selectBox.add(option); 81 | 82 | var expiryArray = []; 83 | rows.each(function (rowIndex) { 84 | var t = getExpiryText($(this).find(SENSIBULL_DOM_PATHS.domForPositionExpiry).text()); 85 | console.log('bs: t: ' + t); 86 | if (t && !expiryArray.includes(t)) { 87 | expiryArray.push(t); 88 | 89 | var option = document.createElement("option"); 90 | option.text = t; 91 | option.value = t; 92 | selectBox.add(option); 93 | } 94 | }); 95 | 96 | //$('.jss1557').click(); 97 | var t = $(SENSIBULL_DOM_PATHS.domForPlacingToggleSelectBox); 98 | $(t[t.length - 1]).before(selectBox); 99 | 100 | selectBox.addEventListener("click", function () { 101 | console.log(this.value); 102 | var selectedItem = this.value; 103 | 104 | var rows = $(SENSIBULL_DOM_PATHS.domForPositionsRows); 105 | rows.each(function (rowIndex) { 106 | var t = getExpiryText($(this).find(SENSIBULL_DOM_PATHS.domForPositionExpiry).text()); 107 | 108 | console.log('bs: text to cmp' + t); 109 | if (selectedItem == 'All' || (t && t == selectedItem)) { 110 | console.log('match found ' + t); 111 | var c = $(this).find(SENSIBULL_DOM_PATHS.domForCheckbox)[0].click(); 112 | console.log(c); 113 | //console.log($(c[0]).attr('class')); 114 | //document.getElementsByClassName($(c[0]).attr('class'))[0].click(); 115 | } 116 | }); 117 | 118 | //selectBox.value = 'All'; 119 | }); 120 | 121 | } 122 | 123 | function getExpiryText(fullText) { 124 | //var expiry = fullText.split(' '); 125 | //var t = expiry[0] + ' ' + expiry[1]; 126 | 127 | return fullText; 128 | } 129 | 130 | tEv("sensibull", "visit", "main", ""); -------------------------------------------------------------------------------- /betterSharekhan.meta.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterSharekhan 3 | // @namespace https://github.com/amit0rana/betterSharekhan 4 | // @version 0.07 5 | // @description Introduces small features on top of newtrade.sharekhan.com 6 | // @author Amit 7 | // @match https://newtrade.sharekhan.com/* 8 | // @grant GM_setValue 9 | // @grant GM_getValue 10 | // @grant GM_addStyle 11 | // @grant GM_registerMenuCommand 12 | // @require https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js 13 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 14 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 15 | // @require https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js 16 | // @require https://raw.githubusercontent.com/kawanet/qs-lite/master/dist/qs-lite.min.js 17 | // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js 18 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.user.js 19 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.meta.js 20 | // ==/UserScript== -------------------------------------------------------------------------------- /betterSharekhan.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterSharekhan 3 | // @namespace https://github.com/amit0rana/betterSharekhan 4 | // @version 0.07 5 | // @description Introduces small features on top of newtrade.sharekhan.com 6 | // @author Amit 7 | // @match https://newtrade.sharekhan.com/* 8 | // @grant GM_setValue 9 | // @grant GM_getValue 10 | // @grant GM_addStyle 11 | // @grant GM_registerMenuCommand 12 | // @require https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js 13 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 14 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 15 | // @require https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js 16 | // @require https://raw.githubusercontent.com/kawanet/qs-lite/master/dist/qs-lite.min.js 17 | // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.27.0/moment.min.js 18 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.user.js 19 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterSharekhan.meta.js 20 | // ==/UserScript== 21 | 22 | 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}; 23 | 24 | window.jQ=jQuery.noConflict(true); 25 | const VERSION = "v0.07"; 26 | const GMPositionsName = "BK_POSITIONS"; 27 | const D_LEVEL = D_LEVEL_DEBUG; 28 | 29 | const allDOMPaths = { 30 | //document.querySelector("#sort > tbody > tr:nth-child(2)") 31 | PathForPositions : "#sort > tbody > tr" 32 | }; 33 | 34 | const positions = initPositions(); 35 | 36 | //main(); 37 | 38 | function initPositions() { 39 | var defaultPositions = { 40 | }; 41 | var positions = GM_getValue(GMPositionsName,defaultPositions); 42 | 43 | GM_registerMenuCommand("Add to or Create new Strategy", function() { 44 | var selectedPositions = jQ(allDOMPaths.PathForPositions).filter(':has(:checkbox:checked)'); 45 | info("selected ps "+ selectedPositions.length); 46 | 47 | var storedStrategies = GM_getValue(GMPositionsName,defaultPositions); 48 | var keys = []; 49 | for (var k in storedStrategies) { 50 | keys.push(k); 51 | } 52 | 53 | var msg = ''; 54 | if (selectedPositions.length <= 0) { 55 | alert('Please select atleast one position to be added to a strategy'); 56 | return; 57 | //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.'; 58 | } 59 | 60 | 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(); 61 | 62 | var strategyName = prompt(msg); 63 | if (strategyName == null) return; 64 | strategyName = strategyName.toUpperCase(); 65 | 66 | if(keys.includes(strategyName)) { 67 | var confirmAdd = confirm("This message is to confirm that there was no typo in strategy name.\n\nYou are adding to an **EXISTING** strategy."); 68 | if (confirmAdd == false) return; 69 | } else { 70 | var confirmAdd = confirm("This message is to confirm that there was no typo in strategy name.\n\nYou are adding to a **NEW** strategy."); 71 | if (confirmAdd == false) return; 72 | } 73 | 74 | var positionArray = []; 75 | selectedPositions.each(function(rowIndex) { 76 | 77 | var span = jQ(this).find("td:nth-child(2) > span"); 78 | var params = JSON.parse(jQ(span).attr('params')); 79 | debug(params); 80 | var text = params.detail.contract; 81 | positionArray.push(text); 82 | }); 83 | 84 | debug(JSON.stringify(storedStrategies)); 85 | 86 | if(keys.includes(strategyName)) { 87 | storedStrategies[strategyName] = storedStrategies[strategyName].concat(positionArray); 88 | } else { 89 | storedStrategies[strategyName] = positionArray; 90 | } 91 | debug(JSON.stringify(storedStrategies)); 92 | 93 | GM_setValue(GMPositionsName,storedStrategies); 94 | window.location.reload(); 95 | }, "s"); 96 | 97 | GM_registerMenuCommand("Delete A Strategy", function() { 98 | var storedStrategies = GM_getValue(GMPositionsName,defaultPositions); 99 | var keys = []; 100 | for (var k in storedStrategies) { 101 | keys.push(k); 102 | } 103 | 104 | var strategyToDelete = prompt("Please specify which strategy do you want to delete: " + keys.toString()); 105 | if (strategyToDelete == null) return; 106 | strategyToDelete = strategyToDelete.toUpperCase(); 107 | 108 | if(keys.includes(strategyToDelete)) { 109 | 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?"); 110 | if (confirmDelete == false) return; 111 | 112 | debug(JSON.stringify(storedStrategies)); 113 | delete storedStrategies[strategyToDelete]; 114 | debug(JSON.stringify(storedStrategies)); 115 | GM_setValue(GMPositionsName,storedStrategies); 116 | 117 | window.location.reload(); 118 | } else { 119 | alert('You do not have any custom strategy with this name. No action taken. Input you gave was: ' + strategyToDelete); 120 | } 121 | 122 | }, "d"); 123 | 124 | return positions; 125 | } 126 | 127 | function createPositionsDropdown() { 128 | var selectBox = document.createElement("SELECT"); 129 | selectBox.id = "tagSelectorP"; 130 | selectBox.classList.add("randomClassToHelpHide"); 131 | 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)" 132 | 133 | var option = document.createElement("option"); 134 | option.text = "All Positions"; 135 | option.value= "All"; 136 | selectBox.add(option); 137 | 138 | var userGeneratedGroups = document.createElement("optgroup"); 139 | userGeneratedGroups.text = "---USER GROUPS---"; 140 | userGeneratedGroups.label = "---USER GROUPS---"; 141 | 142 | selectBox.addEventListener("change", function() { 143 | tEv("sharekhan","positions","filter",""); 144 | var selectedGroup = this.value; 145 | 146 | info("Group selected: " + selectedGroup); 147 | var selectedPositions = positions[selectedGroup]; 148 | 149 | //START work on Positions AREA 150 | var allPositionsRow = jQ(allDOMPaths.PathForPositions); 151 | info('found positions row: ' + allPositionsRow.length); 152 | allPositionsRow.show(); 153 | 154 | 155 | var stocksInList = []; 156 | 157 | //logic to hide the rows in positions table not in our list 158 | var countPositionsDisplaying = 0; 159 | allPositionsRow.addClass("allHiddenRows"); 160 | 161 | var tdymtm = 0.0; 162 | var settledmtm = 0.0; 163 | var totalmtm = 0.0; 164 | var tdybpl = 0.0; 165 | var totalbpl = 0.0; 166 | var selection = []; 167 | 168 | allPositionsRow.each(function(rowIndex) { 169 | try { 170 | var matchFound = false; 171 | var span = jQ(this).find("td:nth-child(2) > span"); 172 | var params = JSON.parse(jQ(span).attr('params')); 173 | debug(params); 174 | 175 | var paramSplit = params.detail.contract.split(" "); 176 | 177 | var contract = params.detail.contract; 178 | var exp = paramSplit[1]; 179 | var scrip = paramSplit[0]; 180 | 181 | if (selectedGroup === "All") { 182 | matchFound = true; 183 | debug('all so showing'); 184 | } else { 185 | if (selectedPositions) { 186 | matchFound = selectedPositions.includes(contract); 187 | if (matchFound) { 188 | debug('custom group found'); 189 | } else { 190 | debug('custom group present but no match'); 191 | } 192 | } else { 193 | debug('custom group not found'); 194 | matchFound = (selectedGroup==exp)?true:false; 195 | if (!matchFound) { 196 | debug('expiry no match'); 197 | matchFound = (selectedGroup==scrip)?true:false; 198 | 199 | if (matchFound) { 200 | debug('scrip match'); 201 | } else { 202 | debug('no match at all'); 203 | } 204 | } else { 205 | debug('exiry match'); 206 | } 207 | } 208 | } 209 | 210 | if (matchFound) { 211 | tdymtm += parseFloat(params.detail.tdymtm); 212 | settledmtm += parseFloat(params.detail.settledmtm); 213 | totalmtm += parseFloat(params.detail.totalmtm); 214 | tdybpl += parseFloat(params.detail.tdybpl); 215 | totalbpl += parseFloat(params.detail.totalbpl); 216 | } else { 217 | jQ(this).hide(); 218 | } 219 | } catch(err) { 220 | } 221 | //END work on Positions AREA 222 | }); 223 | 224 | //document.querySelector("#sort > tbody > tr:nth-child(5) > td:nth-child(16)") today mtm, settled mtm, total mtm, today bpl, total bpm 225 | updatePnlHtml(allPositionsRow, tdymtm, settledmtm, totalmtm, tdybpl, totalbpl); 226 | 227 | return this; 228 | }); 229 | 230 | //add options to the positions select drop down 231 | for(var key in positions){ 232 | option = document.createElement("option"); 233 | option.text = key; 234 | option.value = key; 235 | jQ(userGeneratedGroups).append(option); 236 | }; 237 | 238 | selectBox.add(userGeneratedGroups); 239 | return selectBox; 240 | } 241 | 242 | function updatePnlHtml(allPositionsRow, tdymtm, settledmtm, totalmtm, tdybpl, totalbpl) { 243 | jQ("#sort > thead > tr > th:nth-child(16)").attr("style","width:8%"); 244 | jQ("#sort > thead > tr > th:nth-child(17)").attr("style","width:8%"); 245 | jQ("#sort > thead > tr > th:nth-child(18)").attr("style","width:8%"); 246 | jQ("#sort > thead > tr > th:nth-child(19)").attr("style","width:8%"); 247 | jQ("#sort > thead > tr > th:nth-child(20)").attr("style","width:8%"); 248 | 249 | jQ('#idtodaymtm').remove(); 250 | jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(16)`).append(`
${formatter.format(tdymtm)}
`); 251 | 252 | jQ('#idsettledmtm').remove(); 253 | jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(17)`).append(`
${formatter.format(settledmtm)}
`); 254 | 255 | jQ('#idtotalmtm').remove(); 256 | jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(18)`).append(`
${formatter.format(totalmtm)}
`); 257 | 258 | jQ('#idtdybpl').remove(); 259 | jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(19)`).append(`
${formatter.format(tdybpl)}
`); 260 | 261 | jQ('#idtotalbpl').remove(); 262 | jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(20)`).append(`
${formatter.format(totalbpl)}
`); 263 | } 264 | 265 | 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); 266 | 267 | function showPositionDropdown(retry = true) { 268 | debug('showPositionDropdown'); 269 | 270 | var allPositionsRow = jQ(allDOMPaths.PathForPositions); 271 | 272 | if (allPositionsRow.length < 1) { 273 | debug('sleeping as couldnt find positions'); 274 | //TODO if no positions this will cause loop 275 | if (retry) { 276 | setTimeout(function(){ showPositionDropdown(false); }, 1000); 277 | } 278 | return; 279 | } 280 | 281 | var positionGroupdropdown; 282 | debug(jQ('#tagSelectorP').exists()); 283 | if (jQ('#tagSelectorP').exists()) { 284 | positionGroupdropdown = jQ('#tagSelectorP'); 285 | } else { 286 | positionGroupdropdown = createPositionsDropdown(); 287 | } 288 | 289 | var lastC = positionGroupdropdown.lastChild; 290 | debug(lastC); 291 | if (lastC && lastC.id == "randomForDeleteOptGroupE") { 292 | positionGroupdropdown.removeChild(lastC); 293 | } 294 | 295 | lastC = positionGroupdropdown.lastChild; 296 | debug(lastC); 297 | if (lastC && lastC.id == "randomForDeleteOptGroup") { 298 | positionGroupdropdown.removeChild(lastC); 299 | } 300 | 301 | var optGrp = document.createElement("optgroup"); 302 | optGrp.text = "---SCRIPT WISE---"; 303 | optGrp.label = "---SCRIPT WISE---"; 304 | optGrp.id = "randomForDeleteOptGroup"; 305 | 306 | var optGrpExpiry = document.createElement("optgroup"); 307 | optGrpExpiry.text = "---EXPIRY WISE---"; 308 | optGrpExpiry.label = "---EXPIRY WISE---"; 309 | optGrpExpiry.id = "randomForDeleteOptGroupE"; 310 | 311 | var arrForUnique = []; 312 | var uniqueExpiryArray = []; 313 | allPositionsRow.each(function(rowIndex) { 314 | 315 | var span = jQ(this).find("td:nth-child(2) > span"); 316 | debug(span); 317 | try{ 318 | var params = JSON.parse(jQ(span).attr('params')); 319 | /* 320 | { 321 | "ex": "NSEFO", 322 | "scripcode": "NIFTY 09Dec2021 CE 19000", 323 | "token": "43403", 324 | "detail": { 325 | "netqty": 650, 326 | "netrate": 3.96, 327 | "invsttype": "INVST", 328 | "contract": "NIFTY 09Dec2021 CE 19000", 329 | "scripcode": "NIFTY 09Dec2021 CE 19000", 330 | "ltp": "2.30", 331 | "pricefactor": 1, 332 | "diff": "-0.45", 333 | "prevcloserate": "2.75", 334 | "totalbpl": "0.00", 335 | "acctype": "Bullish", 336 | "sellqty": 0, 337 | "buyrate": 2.4, 338 | "token": "43403", 339 | "settledmtm": "-804.00", 340 | "openrate": 4.09, 341 | "netopenmtm": -803.9999999999999, 342 | "buyqty": 50, 343 | "totalmtm": "-1079.00", 344 | "tdymtm": "-275.00", 345 | "openqty": 600, 346 | "exchange": "NSEFO", 347 | "sellrate": 0, 348 | "tdybpl": "0.00" 349 | } 350 | } 351 | */ 352 | debug(params); 353 | debug(params.scripcode); 354 | 355 | var arr = params.detail.contract.split(" "); 356 | 357 | //creating auto generated script wise grouping 358 | // var ts = params.scripcode; 359 | var ts = arr[0]; 360 | if (!arrForUnique.includes(ts)) { 361 | var option = document.createElement("option"); 362 | option.text = ts; 363 | option.value = ts; 364 | jQ(optGrp).append(option); 365 | arrForUnique.push(ts); 366 | } 367 | 368 | //creating auto generated expiry wise grouping 369 | 370 | 371 | //NIFTY 17Jun2021 PE 14500 372 | var ex = arr[1]; 373 | if (!uniqueExpiryArray.includes(ex)) { 374 | var option2 = document.createElement("option"); 375 | option2.text = ex; 376 | option2.value = ex; 377 | jQ(optGrpExpiry).append(option2); 378 | uniqueExpiryArray.push(ex); 379 | } 380 | } catch(err) { 381 | //debug(err); 382 | } 383 | 384 | }); 385 | 386 | positionGroupdropdown.add(optGrp); 387 | positionGroupdropdown.add(optGrpExpiry); 388 | 389 | 390 | var ourDiv = document.createElement("div"); 391 | ourDiv.classList.add("randomClassToHelpHide"); 392 | ourDiv.classList.add("tagSelectorStyle"); 393 | ourDiv.classList.add("col-xs-2"); 394 | ourDiv.id ='ourDiv'; 395 | 396 | ourDiv.appendChild(positionGroupdropdown); 397 | //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") 398 | 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); 399 | 400 | // simulateSelectBoxEvent(); 401 | 402 | var target = jQ(`#sort > tbody > tr:nth-child(${allPositionsRow.length}) > td:nth-child(18) > b`)[0]; 403 | debug('--------------------------------------------------------'); 404 | 405 | debug(target); 406 | var g_positionsPnlObserver = new MutationObserver(function (mutations) { 407 | var st = null; 408 | mutations.forEach(function (mutation) { 409 | if (mutation.type == "characterData") { 410 | debug("pnl changed"); 411 | updatePnl(); 412 | } 413 | }); 414 | }); 415 | 416 | // Configuration of the observer: 417 | var config = { 418 | attributes:true, 419 | characterData: true, 420 | subtree: true 421 | }; 422 | 423 | // Pass in the target node, as well as the observer options 424 | g_positionsPnlObserver.observe(target, config); 425 | 426 | 427 | } 428 | 429 | function updatePnl() { 430 | debug('pnl changed'); 431 | 432 | var allPositionsRow = jQ(allDOMPaths.PathForPositions); 433 | var allVisibleRows = jQ(allDOMPaths.PathForPositions + ":visible"); 434 | 435 | var tdymtm = 0.0; 436 | var settledmtm = 0.0; 437 | var totalmtm = 0.0; 438 | var tdybpl = 0.0; 439 | var totalbpl = 0.0; 440 | 441 | allVisibleRows.each(function(rowIndex) { 442 | var span = jQ(this).find("td:nth-child(2) > span"); 443 | try{ 444 | var params = JSON.parse(jQ(span).attr('params')); 445 | 446 | tdymtm += parseFloat(params.detail.tdymtm); 447 | settledmtm += parseFloat(params.detail.settledmtm); 448 | totalmtm += parseFloat(params.detail.totalmtm); 449 | tdybpl += parseFloat(params.detail.tdybpl); 450 | totalbpl += parseFloat(params.detail.totalbpl); 451 | 452 | } catch(err) { 453 | } 454 | }); 455 | 456 | //document.querySelector("#sort > tbody > tr:nth-child(5) > td:nth-child(16)") today mtm, settled mtm, total mtm, today bpl, total bpm 457 | updatePnlHtml(allPositionsRow, tdymtm, settledmtm, totalmtm, tdybpl, totalbpl); 458 | 459 | } 460 | 461 | jQ.fn.exists = function () { 462 | return this.length !== 0; 463 | } 464 | 465 | tEv("sharekhan","visit","main",""); 466 | 467 | 468 | function waitForKeyElements ( 469 | selectorTxt, /* Required: The jQuery selector string that 470 | specifies the desired element(s). 471 | */ 472 | actionFunction, /* Required: The code to run when elements are 473 | found. It is passed a jNode to the matched 474 | element. 475 | */ 476 | bWaitOnce, /* Optional: If false, will continue to scan for 477 | new elements even after the first match is 478 | found. 479 | */ 480 | iframeSelector /* Optional: If set, identifies the iframe to 481 | search. 482 | */ 483 | ) { 484 | var targetNodes, btargetsFound; 485 | 486 | if (typeof iframeSelector == "undefined") 487 | targetNodes = jQ(selectorTxt); 488 | else 489 | targetNodes = jQ(iframeSelector).contents () 490 | .find (selectorTxt); 491 | 492 | if (targetNodes && targetNodes.length > 0) { 493 | btargetsFound = true; 494 | /*--- Found target node(s). Go through each and act if they 495 | are new. 496 | */ 497 | targetNodes.each ( function () { 498 | var jThis = jQ(this); 499 | var alreadyFound = jThis.data ('alreadyFound') || false; 500 | 501 | if (!alreadyFound) { 502 | //--- Call the payload function. 503 | var cancelFound = actionFunction (jThis); 504 | if (cancelFound) 505 | btargetsFound = false; 506 | else 507 | jThis.data ('alreadyFound', true); 508 | } 509 | } ); 510 | } 511 | else { 512 | btargetsFound = false; 513 | } 514 | 515 | //--- Get the timer-control variable for this selector. 516 | var controlObj = waitForKeyElements.controlObj || {}; 517 | var controlKey = selectorTxt.replace (/[^\w]/g, "_"); 518 | var timeControl = controlObj [controlKey]; 519 | 520 | //--- Now set or clear the timer as appropriate. 521 | if (btargetsFound && bWaitOnce && timeControl) { 522 | //--- The only condition where we need to clear the timer. 523 | clearInterval (timeControl); 524 | delete controlObj [controlKey] 525 | } 526 | else { 527 | //--- Set a timer, if needed. 528 | if ( ! timeControl) { 529 | timeControl = setInterval ( function () { 530 | waitForKeyElements ( selectorTxt, 531 | actionFunction, 532 | bWaitOnce, 533 | iframeSelector 534 | ); 535 | }, 536 | 300 537 | ); 538 | controlObj [controlKey] = timeControl; 539 | } 540 | } 541 | waitForKeyElements.controlObj = controlObj; 542 | } -------------------------------------------------------------------------------- /betterStocksDeveloper.meta.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterStocksDeveloper 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.1 5 | // @description Small improvemnts to the betterStocks website 6 | // @author Amit 7 | // @match https://web.stocksdeveloper.in/* 8 | // @icon https://www.google.com/s2/favicons?domain=stocksdeveloper.in 9 | // @grant none 10 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 11 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 12 | // ==/UserScript== -------------------------------------------------------------------------------- /betterStocksDeveloper.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterStocksDeveloper 3 | // @namespace http://tampermonkey.net/ 4 | // @version 0.1 5 | // @description Small improvemnts to the betterStocks website 6 | // @author Amit 7 | // @match https://web.stocksdeveloper.in/* 8 | // @icon https://www.google.com/s2/favicons?domain=stocksdeveloper.in 9 | // @grant none 10 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 11 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 12 | // ==/UserScript== 13 | 14 | //(function() { 15 | 'use strict'; 16 | 17 | window.jQ = jQuery.noConflict(true); 18 | 19 | waitForKeyElements("#quantityTable", addBtn); 20 | console.log(jQ("div.pcoded-inner-navbar > ul")); 21 | jQ("div.pcoded-inner-navbar > ul").after('
  • Trade
  • '); 22 | 23 | function addBtn() { 24 | console.log('addBtn'); 25 | 26 | $("#updateQty").remove(); 27 | var i = document.createElement("BUTTON"); 28 | i.type = 'button'; 29 | i.name = 'updateQty'; 30 | i.value = 'Update Quantity'; 31 | i.innerHTML = 'Update Quantity'; 32 | i.id = 'updateQty'; 33 | i.classList.add('btn'); 34 | i.style = 'margin: 0px 2px;'; 35 | i.classList.add('btn-secondary'); 36 | 37 | 38 | i.onclick = function () { 39 | 40 | var selectedGroups = $("tr.selected"); 41 | var accountSizes = []; 42 | var quantities = []; 43 | selectedGroups.each(function (rowIndex) { 44 | var accountSize = jQ(jQ(this).find('td')[1]).text(); 45 | 46 | accountSizes.push(accountSize); 47 | var input = jQ(this).find('input')[0]; 48 | quantities[accountSize] = jQ(input).val(); 49 | }); 50 | 51 | var minAccountSize = Math.min(...accountSizes); 52 | console.log(quantities); 53 | 54 | selectedGroups.each(function (rowIndex) { 55 | var accountSize = jQ(jQ(this).find('td')[1]).text(); 56 | 57 | if (accountSize >= minAccountSize) { 58 | var input = jQ(this).find('input')[0]; 59 | jQ(input).val((accountSize / minAccountSize) * quantities[minAccountSize]); 60 | } 61 | }); 62 | //console.log(min); 63 | 64 | }; 65 | 66 | $("#placeResetButton").before(i); 67 | 68 | } 69 | jQ(document).on('click', "#clearFilter", function (event) { 70 | jQ('#positionsTable_filter > label:nth-child(1) > input:nth-child(1)').val('').trigger('change').click().focus().keydown().keypress().keyup(); 71 | }); 72 | jQ(document).on('dblclick', "#positions-body", function (event) { 73 | jQ("#clearFilter").remove(); 74 | jQ('#positionsTable_filter > label:nth-child(1) > input:nth-child(1)').after("r"); 75 | jQ('#positionsTable_filter > label:nth-child(1) > input:nth-child(1)').val(jQ(event.target).text()).trigger('change').click().focus().keydown().keypress().keyup(); 76 | addFilters(); 77 | }); 78 | //})(); 79 | 80 | waitForKeyElements("#positions-body", addFilters); 81 | 82 | function addFilters() { 83 | var allVisibleRows = jQ("#positions-body > tr"); 84 | var symbols = []; 85 | allVisibleRows.each(function (rowIndex) { 86 | var symbol = jQ(this).find('td:nth-child(1)'); 87 | if (!symbols.contains(symbol)) { 88 | symbols.push(symbol); 89 | } 90 | }); 91 | 92 | console.log(symbols); 93 | } 94 | 95 | 96 | jQ.fn.exists = function () { 97 | return this.length !== 0; 98 | } -------------------------------------------------------------------------------- /betterTradingeconomics.meta.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterTradingeconomics 3 | // @namespace https://github.com/amit0rana/betterTradingeconomics 4 | // @version 0.05 5 | // @description Introduces small features on top of betterTradingeconomics site 6 | // @author Amit 7 | // @match https://tradingeconomics.com/* 8 | // @match https://in.investing.com/* 9 | // @grant GM_addStyle 10 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js 11 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 12 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 13 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterTradingeconomics.user.js 14 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterTradingeconomics.meta.js 15 | // ==/UserScript== 16 | -------------------------------------------------------------------------------- /betterTradingeconomics.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterTradingeconomics 3 | // @namespace https://github.com/amit0rana/betterTradingeconomics 4 | // @version 0.05 5 | // @description Introduces small features on top of betterTradingeconomics site 6 | // @author Amit 7 | // @match https://tradingeconomics.com/* 8 | // @match https://in.investing.com/* 9 | // @grant GM_addStyle 10 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js 11 | // @require https://raw.githubusercontent.com/amit0rana/MonkeyConfig/master/monkeyconfig.js 12 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 13 | // @require https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js 14 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterTradingeconomics.user.js 15 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterTradingeconomics.meta.js 16 | // ==/UserScript== 17 | 18 | // const D_LEVEL = D_LEVEL_NONE; 19 | var currentUrl = window.location.pathname; 20 | console.log(currentUrl); 21 | 22 | //tradingeconomics.com 23 | function stockFilter() { 24 | if (currentUrl.includes('stocks')) { 25 | const domForAdPanel = '[data-ad=ad-below-menu]'; 26 | //$('div#ctl00_ctl07_AdPanel').hide(); 27 | $(domForAdPanel).hide(); 28 | 29 | //jQ(allDOMPaths.PathForPositions).filter(':has(:checkbox:checked)'); 30 | 31 | var statuses = []; 32 | var selectBox = document.createElement("SELECT"); 33 | selectBox.id = "toggleSelectboxID"; 34 | selectBox.classList.add("randomClassToHelpHide"); 35 | //selectBox.style="margin: 15px 0;margin-top: 15px;margin-right: 0px;margin-bottom: 15px;margin-left: 0px;background-color: var(--color-bg-default)" 36 | 37 | var option = document.createElement("option"); 38 | option.text = "Show All"; 39 | option.value= "All"; 40 | selectBox.add(option); 41 | 42 | var statusesRows = $('td#session > span'); 43 | console.log(statusesRows.length); 44 | statusesRows.each(function(rowIndex) { 45 | var title = $(this).attr('title'); 46 | if (title === undefined) { 47 | console.log($(this)); 48 | return; 49 | } 50 | if (!statuses.includes(title)) { 51 | statuses.push(title); 52 | 53 | var option = document.createElement("option"); 54 | option.text = title; 55 | option.value = title; 56 | selectBox.add(option); 57 | } 58 | 59 | }); 60 | console.log(statuses); 61 | 62 | selectBox.addEventListener("change", function() { 63 | var selectedItem = this.value; 64 | 65 | var allRows = $('tr'); 66 | 67 | allRows.each(function(rowIndex) { 68 | if ($(this).find('th').length > 0) { 69 | return; 70 | } 71 | var session = $(this).find(`[title='${selectedItem}']`); 72 | if (selectedItem === 'All' ) { 73 | $(this).show(); 74 | } else if (session.length > 0) { 75 | $(this).show(); 76 | } else { 77 | $(this).hide(); 78 | } 79 | 80 | }); 81 | 82 | }); 83 | 84 | $('#toggleSelectboxID').remove(); 85 | //$(domForAdPanel).after(selectBox); 86 | // $(".col-lg-10 > div:nth-child(5)").before(selectBox); 87 | $("div.v-container").after(selectBox); 88 | 89 | var refreshBtn = document.createElement("button"); 90 | refreshBtn.id = "refreshID"; 91 | refreshBtn.type = "button"; 92 | refreshBtn.innerHTML = "Refresh"; 93 | refreshBtn.addEventListener("click", function() { 94 | var allRows = $('tr'); 95 | 96 | allRows.each(function(rowIndex) { 97 | $(this).show(); 98 | }); 99 | stockFilter(); 100 | }); 101 | $('#refreshID').remove(); 102 | $("#toggleSelectboxID").after(refreshBtn); 103 | } 104 | } 105 | 106 | setTimeout(() => { 107 | stockFilter(); 108 | },5000); 109 | // stockFilter(); 110 | 111 | //investing.in 112 | function hideAds() { 113 | $('div.ad-container').hide(); 114 | } 115 | setTimeout(() => { 116 | hideAds(); 117 | },5000); 118 | -------------------------------------------------------------------------------- /betterUpstox.md: -------------------------------------------------------------------------------- 1 | # betterUpstox 2 | 3 | June 4 21 4 | I have stopped trading on Upstox so this script is not updated. I am told it doesn't work anymore. 5 | 6 | This userscript adds simple features on https://pro.upstox.com/trading UI 7 | 8 | * Problem: We have several positions open at one time and it gets difficult to only focus on position belonging to specific strategy. 9 | * 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. 10 | * Once you filter, you can see number of positions in that strategy and total P&L 11 | 12 | ![usage](https://dl.dropbox.com/s/18ik0c8vffg1t4x/betterUpstoxUsageGif.gif?dl=0) 13 | (if you can't see the image above, click here: https://dl.dropbox.com/s/18ik0c8vffg1t4x/betterUpstoxUsageGif.gif?dl=0) 14 | 15 | # Installation 16 | 17 | Follow below mentioned steps 18 | * 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) 19 | * Open [this link](https://github.com/amit0rana/betterOptionsTrading/raw/master/betterUpstox.user.js) in a new tab. Or copy paste 20 | * Tampermonkey will automatically identify the file as userscript and give you option to install. 21 | * Click on 'Install' button. 22 | * You can verify the installation by clicking on Tampermonkey icon (next to address bar) and then go to 'Dashboard'. You should see 'betterUpstox' installed. 23 | * Now just go to (if the page is already open, then refresh it) and start using. 24 | 25 | ![filter](https://dl.dropbox.com/s/yt8wc31kcqxjw6w/betterUpstoxFilter.png?dl=0) 26 | (if you can't see the image above click here: https://dl.dropbox.com/s/yt8wc31kcqxjw6w/betterUpstoxFilter.png?dl=0) 27 | 28 | 29 | # Note 30 | * Steps to disable the script 31 | 32 | ![disable](https://dl.dropbox.com/s/pp7bqiyzwak6cjp/troubleshootDisableBetterUpstoxVideo.gif?dl=0) 33 | (if you can't see the image above click here: https://dl.dropbox.com/s/pp7bqiyzwak6cjp/troubleshootDisableBetterUpstoxVideo.gif?dl=0) 34 | 35 | * How to upgrade to next version. 36 | 37 | ![upgrade](https://dl.dropbox.com/s/8urhzpu6x78yxhk/upgradeScriptBetterUpstox.png?dl=0) 38 | (if you can't see the image above click here: https://dl.dropbox.com/s/8urhzpu6x78yxhk/upgradeScriptBetterUpstox.png?dl=0) 39 | * Do not forget to refresh the page after update 40 | 41 | * P&L doesn't update on realtime. It shows the P&L at the time of filter. -------------------------------------------------------------------------------- /betterUpstox.meta.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterUpstox 3 | // @namespace https://github.com/amit0rana/betterUpstox 4 | // @version 0.02 5 | // @description Introduces small features on top of pro.upstox app 6 | // @author Amit 7 | // @match https://pro.upstox.com/* 8 | // @grant GM_setValue 9 | // @grant GM_getValue 10 | // @grant GM_registerMenuCommand 11 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 12 | // @require https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js 13 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 14 | // @downloadURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterUpstox.user.js 15 | // @updateURL https://github.com/amit0rana/betterOptionsTrading/raw/master/betterUpstox.meta.js 16 | // ==/UserScript== -------------------------------------------------------------------------------- /betterUpstox.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @name betterUpstox 3 | // @namespace https://github.com/amit0rana/betterUpstox 4 | // @version 0.02 5 | // @description Introduces small features on top of pro.upstox app 6 | // @author Amit 7 | // @match https://pro.upstox.com/* 8 | // @grant GM_setValue 9 | // @grant GM_getValue 10 | // @grant GM_registerMenuCommand 11 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js 12 | // @require https://raw.githubusercontent.com/amit0rana/betterOptionsTrading/master/betterCommon.js 13 | // @require https://gist.github.com/raw/2625891/waitForKeyElements.js 14 | // @downloadURL https://github.com/amit0rana/betterKite/raw/master/betterUpstox.user.js 15 | // @updateURL https://github.com/amit0rana/betterKite/raw/master/betterUpstox.meta.js 16 | // ==/UserScript== 17 | 18 | //window.jQ=jQuery.noConflict(true); 19 | 20 | const D_LEVEL = D_LEVEL_INFO; 21 | 22 | waitForKeyElements ("div._headerLinks_12987", main); 23 | //main(); 24 | 25 | function init() { 26 | debug('initialising'); 27 | var toggleLink = document.createElement("a"); 28 | toggleLink.classList.add("randomClassToDelete"); 29 | toggleLink.classList.add("_link_12987"); 30 | toggleLink.id = "toggleLinkId"; 31 | 32 | toggleLink.innerText = "Filter"; 33 | var tag = $("#root > div > div > div._header_12987 > div._headerLinks_12987"); 34 | debug(tag); 35 | 36 | tag.append(toggleLink); 37 | 38 | var spanForCount = document.createElement("span"); 39 | spanForCount.classList.add("randomClassToDelete"); 40 | spanForCount.classList.add("_link_12987"); 41 | spanForCount.id = "positionText"; 42 | //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;" 43 | 44 | tag.append(spanForCount); 45 | } 46 | 47 | function toggleFilter() { 48 | if (filterActive) { 49 | filterActive = false; 50 | $("#toggleLinkId").text("Filter"); 51 | } else { 52 | filterActive = true; 53 | $("#toggleLinkId").text("Reset"); 54 | } 55 | } 56 | 57 | var filterActive = false; 58 | 59 | function main() { 60 | init(); 61 | 62 | $(document).on('click', "#toggleLinkId", function () { 63 | debug('click'); 64 | $(".allHiddenRows").show(); 65 | if (filterActive) { 66 | debug('deactivating filter'); 67 | toggleFilter(); 68 | //jQ(".randomClassToDelete").remove(); 69 | $("#positionText").text(""); 70 | } else { 71 | debug('activating filter'); 72 | var watchlistRowsDom = $("#watchlistTestId > div > div"); 73 | debug("number " + watchlistRowsDom.length); 74 | if (watchlistRowsDom.length > 0) { 75 | var watchlistArray = []; 76 | watchlistRowsDom.each(function(rowIndex) { 77 | var name = $(this).find("div._name_6136e").text(); 78 | if (name != "") { 79 | var symbol = $(this).find("div._symbol_6136e").text() 80 | 81 | watchlistArray.push(name+symbol); 82 | debug(name + " " + symbol); 83 | } 84 | 85 | }); 86 | 87 | var pnl = 0; 88 | var positionRowsDom = $("#books > div > div > div > div > div > div > div > table > tbody > tr"); 89 | debug("number " + positionRowsDom.length); 90 | if (positionRowsDom.length > 0) { 91 | positionRowsDom.addClass("allHiddenRows"); 92 | var numberOfPositions = 0; 93 | positionRowsDom.each(function(rowIndex) { 94 | var name = $(this).find("div._symbolName_2f3f1").text(); 95 | if (name != "") { 96 | var symbol = $(this).find("div._category_2f3f1").text() 97 | 98 | var workingItem = name+symbol; 99 | if (watchlistArray.includes(workingItem)) { 100 | //all good show. 101 | var p = $(this).find("[data-id=booksOverallPnL]").text().split(",").join(""); 102 | pnl += parseFloat(p); 103 | debug(p); 104 | numberOfPositions++; 105 | debug(workingItem); 106 | } else { 107 | $(this).hide(); 108 | } 109 | } 110 | 111 | }); 112 | if (numberOfPositions > 0 ) { 113 | $("#positionText").text("("+numberOfPositions+") " + formatter.format(pnl)); 114 | } 115 | } 116 | 117 | } 118 | toggleFilter(); 119 | } 120 | }); 121 | 122 | } -------------------------------------------------------------------------------- /minimalGA.js: -------------------------------------------------------------------------------- 1 | var context = window; 2 | var options = "{ anonymizeIp: true, colorDepth: true, characterSet: true, screenSize: true, language: true}"; 3 | 4 | const hhistory = context.history; 5 | const doc = document; 6 | const nav = navigator || {}; 7 | const storage = localStorage; 8 | const encode = encodeURIComponent; 9 | const pushState = hhistory.pushState; 10 | const typeException = 'exception'; 11 | const generateId = () => Math.random().toString(36); 12 | const getId = () => { 13 | if (!storage.cid) { 14 | storage.cid = generateId() 15 | } 16 | return storage.cid; 17 | }; 18 | const serialize = (obj) => { 19 | var str = []; 20 | for (var p in obj) { 21 | if (obj.hasOwnProperty(p)) { 22 | if(obj[p] !== undefined) { 23 | str.push(encode(p) + "=" + encode(obj[p])); 24 | } 25 | } 26 | } 27 | return str.join("&"); 28 | }; 29 | const track = ( 30 | type, 31 | eventCategory, 32 | eventAction, 33 | eventLabel, 34 | eventValue, 35 | exceptionDescription, 36 | exceptionFatal 37 | ) => { 38 | const url = 'https://www.google-analytics.com/collect'; 39 | const data = serialize({ 40 | v: '1', 41 | ds: 'web', 42 | aip: options.anonymizeIp ? 1 : undefined, 43 | tid: "UA-176741575-1", 44 | cid: getId(), 45 | t: type || 'pageview', 46 | sd: options.colorDepth && screen.colorDepth ? `${screen.colorDepth}-bits` : undefined, 47 | dr: doc.referrer || undefined, 48 | dt: doc.title, 49 | dl: doc.location.origin + doc.location.pathname + doc.location.search, 50 | ul: options.language ? (nav.language || "").toLowerCase() : undefined, 51 | de: options.characterSet ? doc.characterSet : undefined, 52 | sr: options.screenSize ? `${(context.screen || {}).width}x${(context.screen || {}).height}` : undefined, 53 | vp: options.screenSize && context.visualViewport ? `${(context.visualViewport || {}).width}x${(context.visualViewport || {}).height}` : undefined, 54 | ec: eventCategory || undefined, 55 | ea: eventAction || undefined, 56 | el: eventLabel || undefined, 57 | ev: eventValue || undefined, 58 | exd: exceptionDescription || undefined, 59 | exf: typeof exceptionFatal !== 'undefined' && !!exceptionFatal === false ? 0 : undefined, 60 | }); 61 | 62 | if(nav.sendBeacon) { 63 | nav.sendBeacon(url, data) 64 | } else { 65 | var xhr = new XMLHttpRequest(); 66 | xhr.open("POST", url, true); 67 | xhr.send(data); 68 | } 69 | }; 70 | const tEv = (category, action, label, value) => track('event', category, action, label, value); 71 | const tEx = (description, fatal) => track(typeException, null, null, null, null, description, fatal); 72 | hhistory.pushState = function (state) { 73 | if (typeof history.onpushstate == "function") { 74 | hhistory.onpushstate({ state: state }); 75 | } 76 | setTimeout(track, options.delay || 10); 77 | return pushState.apply(hhistory, arguments); 78 | } 79 | track(); 80 | context.ma = { 81 | tEv, 82 | tEx 83 | } 84 | 85 | //https://support.google.com/analytics/answer/1033068?hl=en -------------------------------------------------------------------------------- /minimalGA.min.js: -------------------------------------------------------------------------------- 1 | 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}; --------------------------------------------------------------------------------