├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── LICENSE ├── README.md ├── bte-acc-ao-smio.pine ├── bte-acc-ao.pine ├── bte-acc.pine ├── bte-ao.pine ├── bte-strat.pine ├── bte.pine ├── freqtrade └── renko │ └── threads.md ├── fsociety.code-workspace ├── origin.pine ├── pbe.pine ├── renko-acc.pine ├── signal-ext ├── ao-signal.pine ├── fg-cycle-wip.pine ├── fg-cycle.pine ├── signal-bte.pine └── signal.pine └── study-dynamic-variable-alert.pine /.gitignore: -------------------------------------------------------------------------------- 1 | gpg-key.txt 2 | .vscode/settings.json 3 | bte-v5-kiosefftrading.pine 4 | newrelic_agent.log 5 | *.log 6 | freqtrade/renko/threads.md 7 | simulation/bnbperp 8 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "leonardssh.vscord", 4 | "njpwerner.autodocstring", 5 | "batisteo.vscode-django", 6 | "waderyan.gitblame", 7 | "donjayamanne.githistory", 8 | "arturock.gitstash", 9 | "knisterpeter.vscode-github", 10 | "github.vscode-pull-request-github", 11 | "codezombiech.gitignore", 12 | "eamodio.gitlens", 13 | "wholroyd.jinja", 14 | "ms-toolsai.jupyter", 15 | "jeylanib.pine-editor-themes", 16 | "jeylanib.pinescript", 17 | "ms-python.vscode-pylance", 18 | "ms-python.python", 19 | "donjayamanne.python-environment-manager", 20 | "donjayamanne.python-extension-pack", 21 | "kevinrose.vsc-python-indent", 22 | "visualstudioexptteam.vscodeintellicode", 23 | "dsebastien.vscode-git-pack", 24 | "andyyaldoo.vscode-json", 25 | "mhutchie.git-graph", 26 | "CodeStream.codestream" 27 | ] 28 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "pwa-msedge", 9 | "request": "launch", 10 | "name": "Launch Edge against localhost", 11 | "url": "http://localhost:8080", 12 | "webRoot": "${workspaceFolder}" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rpc.appName": "Visual Studio Code", 3 | "rpc.buttonEnabled": true, 4 | "explorer.experimental.fileNesting.enabled": false, 5 | "git.enableCommitSigning": true 6 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # backtesting-trading-engine-pinecoders 2 | Masterpiece of PineCoders script https://www.tradingview.com/script/dYqL95JB-Backtesting-Trading-Engine-PineCoders/ in v3, this is the project to upgrade it to v4-v5 and implement dynamic variable alerts message from within an indicator() alertcondition as discussed here https://t.me/pinescripters/31982 3 | -------------------------------------------------------------------------------- /freqtrade/renko/threads.md: -------------------------------------------------------------------------------- 1 | https://github.com/freqtrade/freqtrade-strategies/issues/59 -------------------------------------------------------------------------------- /fsociety.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.autoSave": "onFocusChange" 9 | }, 10 | "extensions": { 11 | "recommendations": [ 12 | "wholroyd.jinja", 13 | "ms-toolsai.jupyter", 14 | "ms-toolsai.jupyter-keymap", 15 | "ms-toolsai.jupyter-renderers", 16 | "jeylanib.pine-editor-themes", 17 | "jeylanib.pinescript", 18 | "ms-python.vscode-pylance", 19 | "ms-python.python", 20 | "donjayamanne.python-environment-manager", 21 | "donjayamanne.python-extension-pack", 22 | "kevinrose.vsc-python-indent", 23 | "visualstudioexptteam.vscodeintellicode", 24 | "dsebastien.vscode-git-pack", 25 | "andyyaldoo.vscode-json" 26 | ] 27 | } 28 | } -------------------------------------------------------------------------------- /pbe.pine: -------------------------------------------------------------------------------- 1 | // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ 2 | // © pAulseperformance 3 | 4 | // 1142: Updated to version 5 5 | // 1143: Set to overlay true 6 | // 1145: Fixed Fees bug. Fixed Entry A and B reversal switch. 7 | // 1158: Added labels for entry/exits. 8 | 9 | // Portfolio 10 | 11 | // Net Profit (NP) - Shows profitability. 12 | 13 | // Number of Trades (#T) - Shows # of trades taken during backtesting period 14 | 15 | // Average Trade Length (ATL) - Shows avg # of days in a trade 16 | 17 | // Maximum Drawdown (MD) - Max peak-to-valley equity drawdown during backtesting period. This number defines the minimum amount of capital required to trade the system. Typically, this shouldn’t be lower than 34% and we will want to allow for at least 50% beyond this number. 18 | 19 | // Maximum Loss (ML) - Shows largest loss experienced on a per-trade basis. Normally, don’t want to exceed more than 1-2 % of equity. 20 | 21 | // Maximum Drawdown Duration (MDD) - The longest duration of a drawdown in equity prior to a new equity peak. This number is important to help us psychologically understand how long we can expect to wait for a new peak in account equity. 22 | 23 | // Maximum Consecutive Losses (MCL) - The max consecutive losses endured throughout the backtesting period. Another important metric for trader psychology, this will help you understand how many losses you should be prepared to handle. 24 | 25 | // Profit to Maximum Drawdown (P:MD) - A ratio for the average profit to the maximum drawdown. The higher the ratio is, the better. Large profits and small losses contribute to a good PMD. This metric allows us to examine the profit with respect to risk. 26 | 27 | // Profit Loss Ratio (P:L) - Average profit over the average loss. Typically this number should be higher in trend following systems. Mean reversion systems show lower values, but compensate with a better win %. 28 | 29 | // Percent Winners (% W) - The percentage of winning trades. Trend systems will usually have lower win percentages, since statistically the market is only trending roughly 30% of the time. Mean reversion systems typically should have a high % W. 30 | 31 | // Time Percentage (Time %) - The amount of time that the system has an open position. The more time you are in the market, the more you are exposed to the market's risk, not to mention you could be using that money for something else right? 32 | 33 | // Return on Investment (ROI) - Your Net Profit over your initial investment, represented as a percentage. You want this number to be positive and high. 34 | 35 | // Open Profit (OP) - If the strategy has any open positions, the floating value will be represented here. 36 | 37 | // Trading Days (TD) - An important metric showing how many days the strategy was active. This is good to know and will be valuable in understanding how long you will need to run this strategy in order to achieve results. 38 | 39 | 40 | //@version=5 41 | indicator('Portfolio Backtester', overlay=true, precision=6, max_lines_count=500, max_labels_count=500) 42 | 43 | 44 | 45 | 46 | // ——————————————— Inputs and states 47 | // ————— A. Option list constants. 48 | // Trade Direction 49 | TD1 = 'Both' 50 | TD2 = 'Longs Only' 51 | TD3 = 'Shorts Only' 52 | // Strategies 53 | EN0 = '0. None' 54 | EN1 = '1. Entry Strategy A' 55 | EN2 = '2. Entry Strategy B' 56 | 57 | // Entry Stops 58 | ES0 = '0. None' 59 | ES2 = '2. ATR multiple' 60 | ES3 = '3. Fixed %' 61 | ES4 = '4. Fixed Value' 62 | 63 | // In Trade Stops 64 | IS0 = '0. None' 65 | IS1 = '1. Trailing at X multiple' 66 | IS2 = '2. Trailing at %' 67 | IS3 = '3. Trailing at Fixed Value' 68 | IS4 = '4. Move To Break Even' 69 | IS5 = '5. ATR multiple' 70 | IS8 = '8. Last Lo/Hi' 71 | // In Trade Stop Kick In 72 | IK1 = '1. Stop >= Entry Stop' 73 | IK2 = '2. Stop >= Entry +/- X multiple' 74 | IK3 = '3. Stop >= Entry +/- %' 75 | IK4 = '4. Stop >= Entry +/- Fixed Value' 76 | IK5 = '5. Price >= Entry +/- %' 77 | // Pyramiding 78 | PY1 = 'Off' 79 | PY3 = 'On' 80 | PT1 = '1. Every X Multiple' 81 | PT2 = '2. Every % Increment' 82 | PT3 = '3. Every Price Increment' 83 | PT4 = '4. On same entry' 84 | PT5 = '5. On different entry' 85 | PT6 = '6. At every opportunity' 86 | // Slippage 87 | SL1 = '1. None' 88 | SL2 = '2. Percentage' 89 | SL3 = '3. Fixed Value' 90 | // Fees 91 | FE1 = '1. None' 92 | FE2 = '2. Percentage' 93 | FE3 = '3. Fixed Value' 94 | // Position Sizing 95 | PS1 = '1. Proportional to Stop -> Variable' 96 | PS2 = '2. % of Equity -> Variable' 97 | PS3 = '3. % of Capital -> Fixed' 98 | 99 | 100 | // ————— C. Entries 101 | TradeDirection = input.string(TD1, 'Trade Direction', options=[TD1, TD2, TD3], group='Entries') 102 | GenerateLongs = TradeDirection != TD3 103 | GenerateShorts = TradeDirection != TD2 104 | EntryA = input.string(EN1, 'Entry Strategy A', options=[EN0, EN1, EN2], group='Entries', inline='EA') 105 | EntryAReverse = input.bool(false, 'Reverse Signals', tooltip='Long Signals will be interpreted as Short Signals and vise versa', inline='EA', group='Entries') 106 | EntryB = input.string(EN0, 'Entry Strategy B', options=[EN0, EN1, EN2], group='Entries', inline='EB') 107 | EntryBReverse = input.bool(false, 'Reverse Signals', tooltip='Long Signals will be interpreted as Short Signals and vise versa', inline='EB', group='Entries') 108 | 109 | 110 | // Split the random entries in 2 so we can generate from 2 different seeds. 111 | EntryType1 = EntryA == EN1 or EntryB == EN1 112 | EntryType2 = EntryA == EN2 or EntryB == EN2 113 | 114 | 115 | // ————— E. Entry stops 116 | EntryStopType = input.string(ES0, 'Entry Stop Selection', options=[ES0, ES2, ES3], group='Entry Stops') 117 | EntryStopType0 = EntryStopType == ES0 118 | EntryStopType2 = EntryStopType == ES2 119 | EntryStopType3 = EntryStopType == ES3 120 | EntryStopType2Mult = input.float(1.5, '  2. ATR', minval=0.05, step=0.1, group='Entry Stops', inline='ES2') 121 | EntryStopType2Len = input.int(14, '', minval=2, group='Entry Stops', inline='ES2', tooltip='ATR Multiple, ATR Length') 122 | EntryStopType3Pct = input.float(3.0, '  3. Fixed %', minval=0.0001, step=0.1, group='Entry Stops') 123 | 124 | 125 | // ————— D. Filters 126 | FilterType1 = input.bool(false, '1. Random', group='Filters') 127 | FilterType2 = input.bool(false, '2. MACD', inline='FT2', group='Filters', tooltip='Long Filter: MACD > 0\nShort Filter: MACD < 0') 128 | FilterType2Len1 = input.int(12, '', inline='FT2', group='Filters') 129 | FilterType2Len2 = input.int(26, '', inline='FT2', group='Filters') 130 | FilterType2Len3 = input.int(9, '', inline='FT2', group='Filters', tooltip='Fast Length, Slow Length, Signal Line') 131 | FilterType3 = input.bool(false, '3. Bar direction=Trade direction', group='Filters', tooltip='Long filter: close>open\nShort Filter: close 249 | DateFilter ? time >= FromDate and time <= ToDate : true 250 | 251 | 252 | // ————— O. Position sizing 253 | PosType = input.string(PS2, 'Position Sizing', options=[PS1, PS2, PS3], group='Position Sizing') 254 | PosType1 = PosType == PS1 255 | PosType2 = PosType == PS2 256 | PosType3 = PosType == PS3 257 | PosTypeCapital = input.float(100000.0, title='Initial Capital (In Units of Currency)', minval=0.0, step=10000, group='Position Sizing', confirm=true) 258 | PosType1Risk = input.float(1.0, title='  1. % of Equity Risked Per Trade', minval=0.0, maxval=100.0, step=0.1, group='Position Sizing', tooltip=' (Stop % * Position)') 259 | PosType1Cap = input.float(50.0, title='  1. Cap on position sizes (% of Equity)', minval=0.0, maxval=100.0, step=10.0, group='Position Sizing') 260 | PosType2Pct = input.float(1.0, title='  2. % of Equity', minval=0.0, maxval=100.0, step=1.0, group='Position Sizing', confirm=true) 261 | PosType3Pct = input.float(10.0, title='  3. % of Initial Capital', minval=0.0, maxval=100.0, step=5.0, group='Position Sizing') 262 | 263 | // ————— P. Risk Tolerance 264 | i_RiskMaxDrawdown = input.float(30.0, 'Max % Of Equity Drawdown Tolerated', confirm=true, tooltip='If DD exceeds this amount, the strategy will continue to run, but an alert will be generated. This also affects the table colors.', group='Risk Tolerance') 265 | i_RiskMaxDrawdownCB = input.float(50.0, 'Max % Of Equity Drawdown Circuit Breaker', confirm=true, tooltip='If DD exceeds this amount, the strategy will stop.', group='Risk Tolerance') 266 | 267 | 268 | 269 | 270 | // ————— Q. Functions 271 | f_roc() => 272 | // Rate of Change Function 273 | 100 * math.log(Exit1Source / Exit1Source[Exit1Length]) 274 | 275 | f_getMA(_maType, _maSource, _maLength) => 276 | if _maType == 'SMA' 277 | ta.sma(_maSource, _maLength) 278 | else if _maType == 'EMA' 279 | ta.ema(_maSource, _maLength) 280 | else if _maType == 'WMA' 281 | ta.wma(_maSource, _maLength) 282 | else if _maType == 'VWMA' 283 | ta.vwma(_maSource, _maLength) 284 | else if _maType == 'RMA' 285 | ta.rma(_maSource, _maLength) 286 | else if _maType == 'SWMA' 287 | ta.swma(_maSource) 288 | 289 | 290 | 291 | 292 | 293 | // ——————————————— Strategies. 294 | f_RSICross() => 295 | rsi = ta.rsi(close, 14) 296 | 297 | Long = ta.crossover(rsi, 30) 298 | Short = ta.crossunder(rsi, 70) 299 | 300 | Signal = Long ? 1 : Short ? -1 : 0 301 | Signal 302 | 303 | 304 | 305 | // Customize these two for your own entry strategies. 306 | // You can add in a function or customize the function itself. 307 | f_entryStrategyA() => 308 | f_RSICross() 309 | 310 | f_entryStrategyB() => 311 | Long = false 312 | Short = false 313 | Signal = Long ? 1 : Short ? -1 : 0 314 | Signal 315 | 316 | 317 | f_strategies(_strategy) => 318 | // Function to aggregate strategies and convert binary signals to bool 319 | // [bool, bool] 320 | 321 | // Signal = 0 322 | Signal = if EntryType1 and _strategy == EN1 323 | f_entryStrategyA() 324 | else if EntryType2 and _strategy == EN2 325 | f_entryStrategyB() 326 | else 327 | 0 328 | 329 | Long = Signal == 1 330 | Short = Signal == -1 331 | [Long, Short] 332 | 333 | // ENTRY Functions 334 | f_getEntry(InTrade, FilterLongOK, FilterShortOK) => 335 | 336 | // ————— Entry 1: Entry Strategy A 337 | [Entry1Long, Entry1Short] = f_strategies(EN1) 338 | 339 | // ————— Entry 2: Entry Strategy B 340 | [Entry2Long, Entry2Short] = f_strategies(EN2) 341 | 342 | // ———————————————————— Get entry number (positive for longs, negative for shorts). 343 | // Distinguish between A and B selections. 344 | EntryNumberA = Entry1Long and EntryA == EN1 ? 1 : 345 | Entry2Long and EntryA == EN2 ? 2 : 346 | Entry1Short and EntryA == EN1 ? -1 : 347 | Entry2Short and EntryA == EN2 ? -2 : 0 348 | 349 | EntryNumberB = Entry1Long and EntryB == EN1 ? 1 : 350 | Entry2Long and EntryB == EN2 ? 2 : 351 | Entry1Short and EntryB == EN1 ? -1 : 352 | Entry2Short and EntryB == EN2 ? -2 : 0 353 | 354 | 355 | // Entry A numbers are as is, entry B numbers get 100 added/subtracted. 356 | EntryNumber = EntryNumberA != 0 ? EntryNumberA : 357 | EntryNumberB != 0 ? EntryNumberB + (EntryNumberB > 0 ? 100 : -100) : 0 358 | EntryNumber := EntryAReverse and EntryNumberA != 0 ? -EntryNumber : 359 | EntryBReverse and EntryNumberB != 0 ? -EntryNumber : EntryNumber 360 | EntryNumber 361 | 362 | // HELPER Functions 363 | OneZero(_bool) => 364 | _bool ? 1 : 0 365 | 366 | 367 | // ————— Converts current chart timeframe into a float minutes value. 368 | f_resInMinutes() => 369 | _resInMinutes = timeframe.multiplier * (timeframe.isseconds ? 1. / 60 : timeframe.isminutes ? 1. : timeframe.isdaily ? 60. * 24 : timeframe.isweekly ? 60. * 24 * 7 : timeframe.ismonthly ? 60. * 24 * 30.4375 : na) 370 | _resInMinutes 371 | 372 | f_barsToDays(_bars) => 373 | _minutes = f_resInMinutes() * _bars 374 | _days = _minutes / 60 / 24 375 | _days 376 | 377 | f_timeToString(_t) => 378 | str.tostring(year(_t), '0000') + '.' + str.tostring(month(_t), '00') + '.' + str.tostring(dayofmonth(_t), '00') + ' ' + str.tostring(hour(_t), '00') + ':' + str.tostring(minute(_t), '00') + ':' + str.tostring(second(_t), '00') 379 | 380 | 381 | // DRAWDOWN Functions 382 | f_getMaxDD(InTradeEquity, _initialCapital) => 383 | // ————— Max drawdown (measured at every in-trade bar). 384 | // This max drawdown calc needs to maintain separate max and min equity points. 385 | // We are re-evaluating the max and min at each in-trade bar. 386 | // All-time in-trade equity high and min. Min resets when new high encountered. 387 | 388 | // Different than strategy tester which is based off trade-by-trade max Cumulative Profit - min cumulative profit. 389 | // More accurate this way becuase we see drawdown during trades. 390 | // Still based on Close-to-close and not high/low 391 | 392 | // ——————————————— Drawdown. 393 | // ————— Max drawdown (using InTradeEquity measured at every bar but only changes during in-trade bars). 394 | EquityMax = 0.0 395 | EquityMax := math.max(InTradeEquity, nz(EquityMax[1])) 396 | 397 | // Reset min when new high encountered. 398 | EquityMin = 0.0 399 | EquityMin := ta.change(EquityMax) ? EquityMax : math.min(InTradeEquity, nz(EquityMin[1])) 400 | 401 | // Max drawdown in equity. Percent 402 | Drawdown = (EquityMin - EquityMax) / EquityMax 403 | MaxDrawdown = 0.0 404 | MaxDrawdown := math.min(Drawdown, nz(MaxDrawdown[1])) 405 | // label.new(bar_index, high, tostring(Drawdown)) 406 | // Drawdown Peak to Trough Equity 407 | MaxPTDrawdown = 0.0 408 | MaxPTDrawdown := math.min((EquityMin - EquityMax) * _initialCapital, nz(MaxPTDrawdown)) 409 | 410 | // Maximum Drawdown Duration (MDD) - The longest duration of a drawdown in equity prior to a new equity peak. 411 | // This number is important to help us psychologically understand how long we can expect to wait for a new peak in account equity. 412 | var int MaxDDDur = 0 413 | var int MaxDDDurCount = 0 414 | MaxDDDurCount := EquityMax > 0 and TradeDateIsAllowed() ? ta.change(EquityMax) ? 0 : MaxDDDurCount + 1 : 0 // Warning has no effect on accuracy 415 | MaxDDDur := math.max(MaxDDDurCount, MaxDDDur) 416 | 417 | 418 | // Maximum Drawdown - Max peak-to-valley equity drawdown during backtesting period. This number defines the minimum amount of capital required to trade the system. Typically, this shouldn’t be lower than 34% and we will want to allow for at least 50% beyond this number. 419 | // MaxDDPct = round(MaxDrawdown*100,2) // Percent 420 | // MaxDDPT = round(MaxPTDrawdown, 2) // Absolute 421 | 422 | 423 | [MaxDrawdown * 100, MaxPTDrawdown, f_barsToDays(MaxDDDur)] 424 | 425 | 426 | f_getMCL(Equity, Trades, All_Losing) => 427 | // Maximum Consecutive Losses (MCL) - The max consecutive losses endured throughout the backtesting period. Another important metric for trader psychology, this will help you understand how many losses you should be prepared to handle in order to achieve netprofit. 428 | var int ConsecutiveLossesCount = 0 429 | ConsecutiveLossesCount := ta.change(Trades) ? ta.change(All_Losing) ? ConsecutiveLossesCount + 1 : 0 : ConsecutiveLossesCount // Warning has no effect on accuracy. 430 | var int MaxConsecutiveLosses = 0 431 | MaxConsecutiveLosses := math.max(ConsecutiveLossesCount, MaxConsecutiveLosses) 432 | MaxConsecutiveLosses 433 | 434 | 435 | // Aggregate All data 436 | f_getData(_initialCapital) => 437 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 438 | // —————————————————————————————————————— 1. Variable Initializations ————————————————————————————————————————————————————— 439 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 440 | // We declare global and trade vars and propagate values for those that need it. 441 | // Vars holding global stats for all trades are divided in 4 groups: Combined (First and pyramided entries), First entries, Pyramided entries and PEA (Post-Exit Analysis). 442 | // They start with All_, First_, Pyr_ or PEA_ respectively. 443 | // So-called local vars hold data relating to one trade at a time. They use the same prefix, without an underscore. 444 | 445 | // We deal with 4 different units in the code: 446 | // 1. Quote currency (quote), i.e. the price on the chart. 447 | // 2. X (X): the stop's amplitude, 448 | // 3. Currency (curr): the currency of the Initial Capital. 449 | // 4. % of Initial Capital (equ): a % representing a fraction of Initial Capital. 450 | 451 | // ———————————————————— A. Global data. 452 | // ————— Global stats (1st and pyramided entries) 453 | // All entries. 454 | All_Entries = 0 455 | All_Entries := nz(All_Entries[1]) 456 | // All fees paid (equ). 457 | All_FeesEqu = 0.0 458 | All_FeesEqu := nz(All_FeesEqu[1]) 459 | // Sum and avg of PLX (P&L expressed as mult of X). 460 | All_PLXTot = 0.0 461 | All_PLXTot := nz(All_PLXTot[1]) 462 | All_PLXAvg = 0.0 463 | All_PLXAvg := nz(All_PLXAvg[1]) 464 | // Total of all Entry position sizes (equ). 465 | All_PositionInTot = 0.0 466 | All_PositionInTot := nz(All_PositionInTot[1]) 467 | // Profit factor. 468 | All_ProfitFactor = 0.0 469 | All_ProfitFactor := nz(All_ProfitFactor[1]) 470 | // All slippage paid (equ). 471 | All_SlipEqu = 0.0 472 | All_SlipEqu := nz(All_SlipEqu[1]) 473 | // Sum and avg of trade lengths. 474 | All_TradeLengthsTot = 0.0 475 | All_TradeLengthsTot := nz(All_TradeLengthsTot[1]) 476 | All_TradeLengthsAvg = 0.0 477 | All_TradeLengthsAvg := nz(All_TradeLengthsAvg[1]) 478 | // All currency volume traded (both entries and exits counted). 479 | All_VolumeTraded = 0.0 480 | All_VolumeTraded := nz(All_VolumeTraded[1]) 481 | // Sum of all winning/losing trades. 482 | All_Winning = 0 483 | All_Winning := nz(All_Winning[1]) 484 | All_Losing = 0 485 | All_Losing := nz(All_Losing[1]) 486 | // Sum of winning/losing trade lengths. 487 | All_WinsTL = 0.0 488 | All_WinsTL := nz(All_WinsTL[1]) 489 | All_LoseTL = 0.0 490 | All_LoseTL := nz(All_LoseTL[1]) 491 | // P&L total (X) of all winning/losing trades. 492 | All_WinsX = 0.0 493 | All_WinsX := nz(All_WinsX[1]) 494 | All_LoseX = 0.0 495 | All_LoseX := nz(All_LoseX[1]) 496 | // P&L total (equ) of all winning/losing trades. 497 | All_WinsEqu = 0.0 498 | All_WinsEqu := nz(All_WinsEqu[1]) 499 | All_LoseEqu = 0.0 500 | All_LoseEqu := nz(All_LoseEqu[1]) 501 | // Average Winning Trade, and Average Losing Trade 502 | All_AvgWin = 0.0 503 | All_AvgWin := nz(All_AvgWin[1]) 504 | All_AvgLos = 0.0 505 | All_AvgLos := nz(All_AvgLos[1]) 506 | // Sum and avg of all stops (quote). 507 | All_XTot = 0.0 508 | All_XTot := nz(All_XTot[1]) 509 | All_XAvg = 0.0 510 | All_XAvg := nz(All_XAvg[1]) 511 | // Sum and avg of all stops (equ). 512 | All_XEquTot = 0.0 513 | All_XEquTot := nz(All_XEquTot[1]) 514 | All_XEquAvg = 0.0 515 | All_XEquAvg := nz(All_XEquAvg[1]) 516 | // Sum and avg of all stops as percentage of fill. 517 | All_XPctTot = 0.0 518 | All_XPctTot := nz(All_XPctTot[1]) 519 | All_XPctAvg = 0.0 520 | All_XPctAvg := nz(All_XPctAvg[1]) 521 | 522 | // ————— First entry stats. 523 | First_Entries = 0 524 | First_Entries := nz(First_Entries[1]) 525 | First_FeesEqu = 0.0 526 | First_FeesEqu := nz(First_FeesEqu[1]) 527 | First_PLXTot = 0.0 528 | First_PLXTot := nz(First_PLXTot[1]) 529 | First_PLXAvg = 0.0 530 | First_PLXAvg := nz(First_PLXAvg[1]) 531 | First_PositionInTot = 0.0 532 | First_PositionInTot := nz(First_PositionInTot[1]) 533 | First_ProfitFactor = 0.0 534 | First_ProfitFactor := nz(First_ProfitFactor[1]) 535 | First_SlipEqu = 0.0 536 | First_SlipEqu := nz(First_SlipEqu[1]) 537 | First_TradeLengthsTot = 0.0 538 | First_TradeLengthsTot := nz(First_TradeLengthsTot[1]) 539 | First_TradeLengthsAvg = 0.0 540 | First_TradeLengthsAvg := nz(First_TradeLengthsAvg[1]) 541 | First_VolumeTraded = 0.0 542 | First_VolumeTraded := nz(First_VolumeTraded[1]) 543 | First_Winning = 0 544 | First_Winning := nz(First_Winning[1]) 545 | First_Losing = 0 546 | First_Losing := nz(First_Losing[1]) 547 | First_WinsTL = 0 548 | First_WinsTL := nz(First_WinsTL[1]) 549 | First_LoseTL = 0 550 | First_LoseTL := nz(First_LoseTL[1]) 551 | // First_WinsX = 0.0, First_WinsX := nz(First_WinsX[1]) 552 | // First_LoseX = 0.0, First_LoseX := nz(First_LoseX[1]) 553 | First_WinsEqu = 0.0 554 | First_WinsEqu := nz(First_WinsEqu[1]) 555 | First_LoseEqu = 0.0 556 | First_LoseEqu := nz(First_LoseEqu[1]) 557 | First_XTot = 0.0 558 | First_XTot := nz(First_XTot[1]) 559 | First_XAvg = 0.0 560 | First_XAvg := nz(First_XAvg[1]) 561 | First_XEquTot = 0.0 562 | First_XEquTot := nz(First_XEquTot[1]) 563 | First_XEquAvg = 0.0 564 | First_XEquAvg := nz(First_XEquAvg[1]) 565 | First_XPctTot = 0.0 566 | First_XPctTot := nz(First_XPctTot[1]) 567 | First_XPctAvg = 0.0 568 | First_XPctAvg := nz(First_XPctAvg[1]) 569 | 570 | // ————— Pyramiding 571 | // All pyramid entries. 572 | Pyr_Entries = 0 573 | Pyr_Entries := nz(Pyr_Entries[1]) 574 | Pyr_PLXTot = 0.0 575 | Pyr_PLXTot := nz(Pyr_PLXTot[1]) 576 | Pyr_PLXAvg = 0.0 577 | Pyr_PLXAvg := nz(Pyr_PLXAvg[1]) 578 | Pyr_ProfitFactor = 0.0 579 | Pyr_ProfitFactor := nz(Pyr_ProfitFactor[1]) 580 | Pyr_VolumeTraded = 0.0 581 | Pyr_VolumeTraded := nz(Pyr_VolumeTraded[1]) 582 | Pyr_Winning = 0 583 | Pyr_Winning := nz(Pyr_Winning[1]) 584 | Pyr_Losing = 0 585 | Pyr_Losing := nz(Pyr_Losing[1]) 586 | Pyr_WinsEqu = 0.0 587 | Pyr_WinsEqu := nz(Pyr_WinsEqu[1]) 588 | Pyr_LoseEqu = 0.0 589 | Pyr_LoseEqu := nz(Pyr_LoseEqu[1]) 590 | 591 | 592 | 593 | // ————— Risk/Equity management. 594 | // Starting Equity in the script is represented as value 1.0. It is converted at display time in units of currency 595 | // corresponding to whatever the user thinks he's using in the "Initial Capital" field. 596 | // Equity = Initial Capital + P&L. If equity reaches 0.0, ruin occurs and no more trades are entered. 597 | Equity = float(na) 598 | Equity := nz(Equity[1], 1.0) 599 | // Current ROI. 600 | ReturnOnEquity = 0.0 601 | ReturnOnEquity := nz(ReturnOnEquity[1]) 602 | // True if ruin occurs. 603 | Ruin = false 604 | Ruin := Ruin[1] 605 | 606 | // ————— Risk Tolerance. 607 | // True if i_RiskMaxDrawdownCB is exceeded 608 | CircuitBreaker = false 609 | CircuitBreaker := CircuitBreaker[1] 610 | 611 | // ————— Close to close max drawdown (only measured from trade close to trade close). 612 | // Max and min close to close equity points, and end of trade max drawdown reached. Reset min when new high encountered. 613 | CToCEquityMax = 0.0 614 | CToCEquityMin = 0.0 615 | CToCDrawdown = 0.0 616 | // Max close to close drawdown in equity. 617 | CToCMaxDrawdown = 0.0 618 | CToCMaxDrawdown := nz(CToCMaxDrawdown[1]) 619 | 620 | 621 | // Equity update in-trade to provide full equity max drawdown instead of close to close drawdown. 622 | InTradeEquity = 0.0 623 | InTradeEquity := nz(InTradeEquity[1]) 624 | InTradeStartEquity = 0.0 625 | InTradeStartEquity := nz(InTradeStartEquity[1]) 626 | 627 | 628 | // ———————————————————— B. Local data. 629 | // "Local" refers to data that is maintained while a trade is open or a PEA is ongoing. 630 | // ————— Trade management. 631 | // Entry level (open of bar following trigger). 632 | EntryOrder = 0.0 633 | EntryOrder := EntryOrder[1] 634 | // Entry price after slippage. 635 | EntryFill = 0.0 636 | EntryFill := EntryFill[1] 637 | // Stop level at Entry. 638 | EntryStop = 0.0 639 | EntryStop := EntryStop[1] 640 | // Entry stop amplitude in quote, from fill to stop (so includes slippage). 641 | entryStopGap = 0.0 642 | entryStopGap := entryStopGap[1] 643 | // Entry stop as % of entry fill. 644 | EntryXPct = 0.0 645 | EntryXPct := EntryXPct[1] 646 | // Entry stop (equ). 647 | EntryXEqu = 0.0 648 | EntryXEqu := EntryXEqu[1] 649 | // Exit level (order). 650 | ExitOrder = 0.0 651 | ExitOrder := ExitOrder[1] 652 | // Exit fill after slippage. 653 | ExitFill = 0.0 654 | ExitFill := ExitFill[1] 655 | // Fees paid on entry (equ). 656 | FeesPaidIn = 0.0 657 | FeesPaidIn := FeesPaidIn[1] 658 | // Fees paid on exit (equ). 659 | FeesPaidOut = 0.0 660 | FeesPaidOut := FeesPaidOut[1] 661 | // Holds last issued entry signal: + for longs, - for shorts. 662 | LastEntryNumber = 0 663 | LastEntryNumber := LastEntryNumber[1] 664 | // Last entry's price level, whether it was a normal or pyramided entry. 665 | LastEntryPrice = 0.0 666 | LastEntryPrice := LastEntryPrice[1] 667 | // Position size at entry (equ). 668 | PositionIn = 0.0 669 | PositionIn := PositionIn[1] 670 | // Position size at exit (equ). 671 | PositionOut = 0.0 672 | PositionOut := PositionOut[1] 673 | // Slippage incurred on entry (quote). 674 | SlipPaidIn = 0.0 675 | SlipPaidIn := SlipPaidIn[1] 676 | // Slippage incurred on exit (quote). 677 | SlipPaidOut = 0.0 678 | SlipPaidOut := SlipPaidOut[1] 679 | // Total trade slippage (equ). 680 | SlippageEqu = 0.0 681 | SlippageEqu := SlippageEqu[1] 682 | // Target level for hard exits. 683 | TakeProfitLevel = 0.0 684 | TakeProfitLevel := TakeProfitLevel[1] 685 | // Trade length in bars. 686 | TradeLength = 0 687 | TradeLength := TradeLength[1] 688 | // Max in-trade drawdown. 689 | TradeMaxDrawdown = 0.0 690 | TradeMaxDrawdown := TradeMaxDrawdown[1] 691 | 692 | EntryNumber = 0 // Current first entry number throughout trade. 693 | // EntryNumberA = 0 // Current first entry number from selecton A. 694 | // EntryNumberB = 0 // Current first entry number from selecton B. 695 | InTradeStop = 0.0 // Last valid in-trade stop (we always use previous bar's stop for comparison to price, so must be used indexed). 696 | MaxReached = 0.0 // Max high/low reached since beginning of trade. 697 | MinReached = 0.0 // Min high/low reached since beginning of trade. 698 | TradeDrawdown = 0.0 // Discrete drawdown at each bar in trade's history, to be used to determine trade max drawdown. 699 | TradePLX = 0.0 // Trade PL (X), after slippage. 700 | // TradePLXMax = 0.0 // Highest PL (X) reached during trade. 701 | // TradePLXMin = 0.0 // Lowest PL (X) reached during trade. 702 | // TradePLPct = 0.0 // Trade PL (%) after slippage. 703 | TradePLEqu = 0.0 // Net trade PL (equ), i.e. including slippage AND fees. 704 | 705 | // ————— Pyramiding. 706 | PyrEntries = 0 707 | PyrEntries := nz(PyrEntries[1]) 708 | PyrEntryBarTot = 0 709 | PyrEntryBarTot := nz(PyrEntryBarTot[1]) 710 | PyrEntryBarAvg = 0.0 711 | PyrEntryBarAvg := nz(PyrEntryBarAvg[1]) 712 | PyrEntryFill = 0.0 713 | PyrEntryFillTot = 0.0 714 | PyrEntryFillTot := nz(PyrEntryFillTot[1]) 715 | PyrEntryFillAvg = 0.0 716 | PyrEntryFillAvg := nz(PyrEntryFillAvg[1]) 717 | PyrEntryOrder = 0.0 718 | PyrEntryX = 0.0 719 | PyrEntryX := nz(PyrEntryX[1]) 720 | PyrEntryXTot = 0.0 721 | PyrEntryXTot := nz(PyrEntryXTot[1]) 722 | PyrEntryXAvg = 0.0 723 | PyrEntryXAvg := nz(PyrEntryXAvg[1]) 724 | PyrEntryXPctTot = 0.0 725 | PyrEntryXPctTot := nz(PyrEntryXPctTot[1]) 726 | PyrEntryXPctAvg = 0.0 727 | PyrEntryXPctAvg := nz(PyrEntryXPctAvg[1]) 728 | PyrFeesEquTot = 0.0 729 | PyrFeesEquTot := nz(PyrFeesEquTot[1]) 730 | PyrFeesPaidIn = 0.0 731 | PyrFeesPaidOut = 0.0 732 | PyrEntryXPct = 0.0 733 | PyrPositionIn = 0.0 734 | PyrPositionOut = 0.0 735 | PyrPositionInTot = 0.0 736 | PyrPositionInTot := nz(PyrPositionInTot[1]) 737 | PyrPositionInAvg = 0.0 738 | PyrPositionInAvg := nz(PyrPositionInAvg[1]) 739 | PyrPositionInQtyTot = 0.0 740 | PyrPositionInQtyTot := nz(PyrPositionInQtyTot[1]) 741 | PyrSlipPaidEqu = 0.0 742 | PyrSlipPaidIn = 0.0 743 | PyrSlipPaidOut = 0.0 744 | PyrSlipEquTot = 0.0 745 | PyrSlipEquTot := nz(PyrSlipEquTot[1]) 746 | PyrTradePLX = 0.0 747 | PyrTradePLPct = 0.0 748 | PyrTradePLEqu = 0.0 749 | PyrTradeLengthsTot = 0 750 | PyrTradeLengthsTot := nz(PyrTradeLengthsTot[1]) 751 | PyrTradeLengthsAvg = 0.0 752 | PyrTradeLengthsAvg := nz(PyrTradeLengthsAvg[1]) 753 | 754 | 755 | // ———————————————————— C. State and misc vars. 756 | // These variables get updated after triggers and during trades. 757 | // They do not require propagation because they are derived from the state of other propagated information or previous states. 758 | EntryTrigger = false // True when an entry trigger is detected. 759 | ExitTrigger = false // True when an exit trigger is detected 760 | PyramidEntry = false // True when a long/short pyramid entry occurs. 761 | InTrade = false // True when in InLong or InShort is true. 762 | ExitCondition = false // True when stop breached, take profit or target level reached. 763 | // ————— Longs 764 | LongEntryTrigger = false // Becomes true on the bar when entry order is to be issued; false otherwise. 765 | InLong = false // True from bar after entry trigger to bar of exit trigger, inclusively. 766 | // ————— Shorts 767 | ShortEntryTrigger = false // Becomes true on the bar when entry order is to be issued; false otherwise. 768 | InShort = false // True from bar after entry trigger to bar of exit trigger, inclusively. 769 | 770 | 771 | // Not sure where to put this. But needs to be propagated 772 | // ------ Stops 773 | StoppedCondition = false 774 | 775 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 776 | // ——————————————————————————————————————————— 3. Entry Stops ————————————————————————————————————————————————————————————— 777 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 778 | // ————— OHLC rounded to mintick (used to standardize values across test as mintick sometimes varies during dataset). 779 | // Although situated in this module, are used all over the script. 780 | Ropen = math.round_to_mintick(open) 781 | Rhigh = math.round_to_mintick(high) 782 | Rlow = math.round_to_mintick(low) 783 | Rclose = math.round_to_mintick(close) 784 | // ————— Lowest/highest of open/close 785 | MinOC = math.round_to_mintick(math.min(open, close)) 786 | MaxOC = math.round_to_mintick(math.max(open, close)) 787 | // ————— Entry Stops init. 788 | EntryStopLong = MinOC 789 | EntryStopShort = MaxOC 790 | // Entry Stops are pre-calculated every bar so they are always at hand upon entering. 791 | // They cannot depend on information like a trade entry level as they must be calculated prior to entry. 792 | // // The entry stop wil be taken over by the In-trade stop when its kick-in rules allow for it. 793 | 794 | // ————— Entry Stop 2: ATR*Multiple 795 | EntryStop2_Atr = EntryStopType2 ? nz(ta.atr(EntryStopType2Len)) : 0 796 | EntryStop2_AtrGap = EntryStop2_Atr * EntryStopType2Mult 797 | EntryStop2Long = MinOC - EntryStop2_AtrGap 798 | EntryStop2Short = MaxOC + EntryStop2_AtrGap 799 | // ————— Entry Stop 3: Fixed Percent 800 | EntryStop3Long = MinOC * (1.0 - EntryStopType3Pct / 100) 801 | EntryStop3Short = MaxOC * (1.0 + EntryStopType3Pct / 100) 802 | 803 | // ———————————————————— Select Entry Stop chosen by user. 804 | EntryStopLong_ = EntryStopType2 ? EntryStop2Long : EntryStopType3 ? EntryStop3Long : 0.0 805 | EntryStopShort_ = EntryStopType2 ? EntryStop2Short : EntryStopType3 ? EntryStop3Short : 9e99 806 | // Round stop to tick size. 807 | EntryStopLong := math.round_to_mintick(EntryStopLong_) 808 | EntryStopShort := math.round_to_mintick(EntryStopShort_) 809 | 810 | 811 | 812 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 813 | // ————————————————————————————————————————————— 4. Filters ——————————————————————————————————————————————————————————————— 814 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 815 | // ** If adding a new filter, be sure to include in Entry for Filter States 816 | 817 | // ————— Filter 1: Random 818 | Filter1Long = FilterType1 ? math.round(math.random(0, 10, 1)) == 1 : true 819 | Filter1Short = FilterType1 ? math.round(math.random(0, 10, 1)) == 1 : true 820 | // ————— Filter 2: MACD Hist 821 | _macdLine = FilterType2 ? nz(f_getMA('EMA', close, FilterType2Len1) - f_getMA('EMA', close, FilterType2Len2)) : 0. 822 | _macdSignal = FilterType2 ? nz(f_getMA('EMA', _macdLine, FilterType2Len3)) : 0. 823 | _macdHist = FilterType2 ? _macdLine - _macdSignal : 0. 824 | Filter2Long = FilterType2 ? _macdHist > 0 : true 825 | Filter2Short = FilterType2 ? _macdHist < 0 : true 826 | 827 | // ————— Filter 3: Bar direction 828 | Filter3Long = FilterType3 ? close > open : true 829 | Filter3Short = FilterType3 ? close < open : true 830 | // ————— Filter 4: Rising volume 831 | Filter4Long = FilterType4 ? ta.rising(volume, 1) : true 832 | Filter4Short = Filter4Long 833 | // ————— Filter 5: Rising/Falling MA 834 | Filter5Ma = FilterType5 ? nz(f_getMA('SMA', close, FilterType5Len)) : 0 835 | Filter5Long = FilterType5 ? ta.rising(Filter5Ma, FilterType5Bars) : true 836 | Filter5Short = FilterType5 ? ta.falling(Filter5Ma, FilterType5Bars) : true 837 | // ————— Filter 6: Maximum risk allowed at entry 838 | Filter6Long = FilterType6 ? close - EntryStopLong < close * FilterType6MaxRisk / 100 : true 839 | Filter6Short = FilterType6 ? EntryStopShort - close < close * FilterType6MaxRisk / 100 : true 840 | // ————— Filter 7: Maximum delta with previous close allowed at entry 841 | Filter7Long = FilterType7 ? close < close[1] * (1 + FilterType7IncPct / 100) : true 842 | Filter7Short = FilterType7 ? close > close[1] * (1 - FilterType7IncPct / 100) : true 843 | // ————— Filter 8: Minimum delta with previous close allowed at entry 844 | Filter8Long = FilterType8 ? close > close[1] * (1 + FilterType8IncPct / 100) : true 845 | Filter8Short = FilterType8 ? close < close[1] * (1 - FilterType8IncPct / 100) : true 846 | 847 | // ————— Filter 9: RSI OS/OB 848 | Rsi = FilterType9 ? ta.rsi(close, FilterType9Len) : 0 849 | RsiOS = Rsi < FilterType9OS 850 | RsiOB = Rsi > FilterType9OB 851 | Filter9Long = FilterType9 ? not RsiOB : true 852 | Filter9Short = FilterType9 ? not RsiOS : true 853 | // ————— Filter 10: Moving Average 854 | Filter10Ma = FilterType10 ? f_getMA(FilterType10MAType, FilterType10Source, FilterType10Len) : 0. 855 | Filter10Long = FilterType10 ? close > Filter10Ma : true 856 | Filter10Short = FilterType10 ? close < Filter10Ma : true 857 | // ————— Filter 10: Chandelier 858 | // Filter10Long = FilterType10? C2Bull : true 859 | // Filter10Short = FilterType10? C2Bear : true 860 | // ————— Filter 11: MA Squize 861 | // Filter11Long = FilterType11? MASqzBull : true 862 | // Filter11Short = FilterType11? MASqzBear : true 863 | 864 | 865 | // ———————————————————— Assemble filters 866 | FilterLongOK = Filter1Long and Filter2Long and Filter3Long and Filter4Long and Filter5Long and Filter6Long and Filter7Long and Filter8Long and Filter9Long and Filter10Long 867 | FilterShortOK = Filter1Short and Filter2Short and Filter3Short and Filter4Short and Filter5Short and Filter6Short and Filter7Short and Filter8Short and Filter9Short and Filter10Short 868 | 869 | 870 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 871 | // ————————————————————————————————————————————— 5. Entries ——————————————————————————————————————————————————————————————— 872 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 873 | 874 | EntryNumber := f_getEntry(InTrade, FilterLongOK, FilterShortOK) 875 | 876 | // ————— Whipsaw Waiting Period. Waits x bars after signal is generated. If an opposite signal is generated the waiting period is reset. 877 | WhipSawWait = input.bool(false, 'WhipSaw Wait', group='Filters', inline='WSW', tooltip='Waits x bars after entry signal is generated. If an opposite signal is generated the waiting period is reset.') 878 | WhipSawLen = input.int(3, '', group='Filters', inline='WSW') 879 | 880 | if WhipSawWait 881 | var _wait = false 882 | var _waiting = 0 883 | barsAgoLong = nz(ta.barssince(EntryNumber > 0)) 884 | barsAgoShort = nz(ta.barssince(EntryNumber < 0)) 885 | // Reset wait period if opposite signal happens within waiting period. 886 | if ta.cross(barsAgoLong, barsAgoShort) 887 | _waiting := 0 888 | _waiting 889 | 890 | var SavedEntryNumber = 0 891 | if EntryNumber != 0 and WhipSawWait 892 | _wait := true 893 | SavedEntryNumber := EntryNumber 894 | SavedEntryNumber 895 | 896 | if _wait 897 | _waiting := _waiting + 1 898 | _waiting 899 | 900 | if _wait and _waiting <= WhipSawLen 901 | EntryNumber := 0 902 | EntryNumber 903 | else if _wait and _waiting > WhipSawLen 904 | EntryNumber := SavedEntryNumber 905 | _waiting := 0 906 | _wait := false 907 | _wait 908 | 909 | 910 | 911 | 912 | 913 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 914 | // ————————————————————————————————— 6. Trade Entry and In-trade Processing ——————————————————————————————————————————————— 915 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 916 | // Here we: 917 | // A. Process 1st entries. 918 | // B. Process pyramided entries. 919 | // C. Update in-trade numbers for display purposes. 920 | 921 | // ———————————————————— Trade state management 922 | // ————— Determine if we have entered a trade and propagate state until we exit. 923 | InLong := LongEntryTrigger[1] or InLong[1] and not ExitCondition[1] 924 | InShort := ShortEntryTrigger[1] or InShort[1] and not ExitCondition[1] 925 | // ————— Merge states for ease of logical testing. 926 | InTrade := InLong or InShort 927 | FirstEntry1stBar = EntryTrigger[1] and not PyramidEntry[1] 928 | PyramidEntry1stBar = EntryTrigger[1] and PyramidEntry[1] 929 | 930 | 931 | // ———————————————————— A. Process 1st entries. 932 | // ————— If a trade's first entry (as opposed to pyramiding entries) must be entered, process it. 933 | // While this code appears earlier in the script than the entry trigger detection that comes in module 10, 934 | // it is only executed at the bar following the one where the trigger is detected. 935 | if FirstEntry1stBar 936 | // ————— Handle trade-opening tasks. 937 | // Suppose market entry order is given at open. 938 | EntryOrder := Ropen 939 | // Begin counting trade length. 940 | TradeLength := 1 941 | // Reset max and min point reached during trade. 942 | MaxReached := InLong ? Rhigh : Rlow 943 | MinReached := InLong ? Rlow : Rhigh 944 | // Save last issued signal number. 945 | LastEntryNumber := EntryNumber[1] 946 | 947 | // Calculate what slippage should be before hi/lo constraints. 948 | SlipPaidIn := SlippageType2 ? math.round_to_mintick(EntryOrder * SlippageType2InPct / 100) : SlippageType3 ? math.round_to_mintick(SlippageType3InVal * syminfo.mintick) : 0.0 949 | // Entry fill price including tentative slippage. 950 | EntryFill := math.max(0.0, EntryOrder + (InLong ? SlipPaidIn : -SlipPaidIn)) 951 | // Cap fill to bar's hi/lo (can't slip more/less that hi/lo). 952 | EntryFill := InLong ? math.min(Rhigh, EntryFill) : math.max(Rlow, EntryFill) 953 | // Calculate actual slippage. 954 | SlipPaidIn := math.abs(EntryFill - EntryOrder) 955 | // Save fill for pyramiding rules validations. 956 | LastEntryPrice := EntryFill 957 | // Initialized upon entry and constant throughout trade, InTradeStop will usually take over in further trade management. 958 | EntryStop := InLong ? EntryStopLong : EntryStopShort 959 | 960 | 961 | // ————— Calculate X upon entry. 962 | // This amplitude in quote currency is the fundamental unit of risk for the trade, which we call X. 963 | // We also keep a version of it expressed as a % of the entry fill, and another in equ. Notice that entry slippage affects the size of X. 964 | entryStopGap := math.abs(EntryFill - EntryStop) 965 | EntryXPct := entryStopGap / EntryFill 966 | 967 | // PositionIn size is determined according to user selection: Type 1 for relative pos size and fixed risk. Type 2 for fixed % of Equity. Type 3 for fixed % of initial capital. 968 | PositionIn := math.min(Equity, PosType1 ? math.min(Equity * PosType1Cap / 100, Equity * PosType1Risk / 100 / EntryXPct) : PosType2 ? Equity * PosType2Pct / 100 : PosType3Pct / 100) 969 | // % Fees are calculated on the PositionIn size. Fixed fees are in equ. Either way they end up being expressed in equ and will only later be deducted from trade's PL and Equity. 970 | FeesPaidIn := FeesType2 ? PositionIn * FeesType2InPct / 100 : 0.0 971 | // X (equ). 972 | EntryXEqu := PositionIn * EntryXPct 973 | // Convert slippage to equ units for display during trade. 974 | SlippageEqu := SlipPaidIn / EntryOrder * PositionIn 975 | 976 | ExitOrder := 0.0 977 | ExitFill := 0.0 978 | TakeProfitLevel := na 979 | TradePLX := 0.0 980 | // TradePLPct := 0.0 981 | TradePLEqu := 0.0 982 | // TradePLXMax := 0.0 983 | // TradePLXMin := 0.0 984 | SlipPaidOut := 0.0 985 | FeesPaidOut := 0.0 986 | TradeMaxDrawdown := 0.0 987 | 988 | // Going to use this temp value to watch for a new maximum equity drawdown during the trade, 989 | // leaving the real Equity unchanged until the end of the trade. 990 | InTradeStartEquity := Equity 991 | InTradeEquity := Equity 992 | InTradeEquity 993 | else 994 | // ———————————————————— B. Process pyramided entries. 995 | if PyramidEntry1stBar 996 | // Count of pyramids in current trade. 997 | PyrEntries := PyrEntries + 1 998 | // Cumulative count of TLs for all pyramided entries. Adds 1 per bar per active entry. 999 | PyrTradeLengthsTot := PyrTradeLengthsTot + PyrEntries 1000 | PyrTradeLengthsAvg := PyrTradeLengthsTot / PyrEntries 1001 | // Suppose market entry order is given at open. 1002 | PyrEntryOrder := Ropen 1003 | // Add slippage to entry. 1004 | PyrSlipPaidIn := SlippageType2 ? math.round_to_mintick(PyrEntryOrder * SlippageType2InPct / 100) : SlippageType3 ? math.round_to_mintick(SlippageType3InVal * syminfo.mintick) : 0.0 1005 | // Entry fill price including slippage. 1006 | PyrEntryFill := math.max(0.0, PyrEntryOrder + (InLong ? PyrSlipPaidIn : -PyrSlipPaidIn)) 1007 | // Cap fill to hi/lo. 1008 | PyrEntryFill := InLong ? math.min(Rhigh, PyrEntryFill) : math.max(Rlow, PyrEntryFill) 1009 | // Update X. Need to use previous bar's in-trade stop as current one isn't calculated yet. 1010 | PyrEntryX := math.abs(PyrEntryFill - InTradeStop[1]) 1011 | // Update X%. 1012 | PyrEntryXPct := PyrEntryX / PyrEntryFill 1013 | // PositionIn size is determined according to user selection: Type 1 for relative pos size and fixed risk. Type 2 for fixed % of Equity. Type 3 for fixed % of initial capital. 1014 | PyrPositionIn := PyramidingPosMult * math.min(Equity, PosType1 ? math.min(Equity * PosType1Cap / 100, Equity * PosType1Risk / 100 / EntryXPct) : PosType2 ? Equity * PosType2Pct / 100 : PosType3Pct / 100) 1015 | // Also keep position size in units of asset. 1016 | PyrPositionInQty = PyrPositionIn * _initialCapital / PyrEntryFill 1017 | // % Fees are calculated on the PositionIn size. Fixed fees are in equ. Either way they end up being expressed in equ and will only later be deducted from trade's PL and Equity. 1018 | PyrFeesPaidIn := FeesType2 ? PyrPositionIn * FeesType2InPct / 100 : 0.0 1019 | // X (equ). 1020 | // PyrEntryXEqu := PyrPositionIn*PyrEntryXPct 1021 | // Convert slippage in equ. 1022 | PyrSlipPaidEqu := PyrSlipPaidIn / PyrEntryOrder * PyrPositionIn 1023 | 1024 | // ————— Build totals and avgs for all pyramided entries in current trade. 1025 | // X (quote) 1026 | PyrEntryXTot := PyrEntryXTot + PyrEntryX 1027 | PyrEntryXAvg := PyrEntryXTot / PyrEntries 1028 | // X % 1029 | PyrEntryXPctTot := PyrEntryXPctTot + PyrEntryXPct 1030 | PyrEntryXPctAvg := PyrEntryXPctTot / PyrEntries 1031 | // Entry positions. 1032 | PyrPositionInTot := PyrPositionInTot + PyrPositionIn 1033 | PyrPositionInAvg := PyrPositionInTot / PyrEntries 1034 | PyrPositionInQtyTot := PyrPositionInQtyTot + PyrPositionInQty 1035 | // Average fill. 1036 | PyrEntryFillTot := PyrEntryFillTot + PyrEntryFill 1037 | PyrEntryFillAvg := PyrEntryFillTot / PyrEntries 1038 | // 1st entry's TL at pyr entry point. Use +1 because it hasn't been incremented yet! 1039 | PyrEntryBarTot := PyrEntryBarTot + TradeLength + 1 1040 | PyrEntryBarAvg := PyrEntryBarTot / PyrEntries 1041 | // Fees & Slippage. 1042 | PyrFeesEquTot := PyrFeesEquTot + PyrFeesPaidIn 1043 | PyrSlipEquTot := PyrSlipEquTot + PyrSlipPaidEqu 1044 | // Save last entry price to then calculate subsequent pyramiding rules. 1045 | LastEntryPrice := PyrEntryFill 1046 | LastEntryPrice 1047 | 1048 | 1049 | // ———————————————————— C. Update in-trade info. Also update some numbers for display purposes. 1050 | if InTrade 1051 | // ————— Update essential in-trade info. 1052 | // Increase trade length. 1053 | TradeLength := TradeLength + 1 1054 | // Update Max point reached during trade (used both for trailing stop reference point and in-trade max drawdown calcs). 1055 | MaxReached := InLong ? math.max(nz(MaxReached[1], Rhigh), Rhigh) : InShort ? math.min(nz(MaxReached[1], Rlow), Rlow) : na 1056 | 1057 | // ————— Update in-trade max drawdown info (has nothing to do with max drawdown calcs later in this section). 1058 | // Since we only use the Min point to calculate in-trade max drawdown, reset it when a new max is found. 1059 | MinReached := ta.change(MaxReached) ? MaxReached : InLong ? math.min(nz(MinReached[1], Rlow), Rlow) : InShort ? math.max(nz(MinReached[1], Rhigh), Rhigh) : na 1060 | // Record current drawdown to build history of all drawdowns during trade. 1061 | TradeDrawdown := math.min(0.0, (MaxReached - MinReached) * (InLong[1] ? -1 : 1) / MaxReached) 1062 | // Find largest drawdown during trade. 1063 | TradeMaxDrawdown := math.min(TradeMaxDrawdown, TradeDrawdown) 1064 | 1065 | // ————— Simulate a complete exit from close here to display provisional trade info. 1066 | // In this "display only mode" during trade, fees paid in or out are ignored. They will only be included in results at real exit. 1067 | ExitOrder := StoppedCondition[1] ? EntryStop : Ropen 1068 | // Calculate tentative slippage from Exit order level. 1069 | SlipPaidOut := SlippageType2 ? math.round_to_mintick(ExitOrder * SlippageType2OutPct / 100) : SlippageType3 ? math.round_to_mintick(SlippageType3OutVal * syminfo.mintick) : 0.0 1070 | // Calculate Exit Fill including slipppage, protecting against negative values. 1071 | ExitFill := math.max(0.0, ExitOrder + (InLong[1] ? -SlipPaidOut : SlipPaidOut)) 1072 | // Fill cannot be outside hi/lo. 1073 | ExitFill := InLong[1] ? math.max(Rlow, ExitFill) : math.min(Rhigh, ExitFill) 1074 | // Calculate slippage that actually occured. 1075 | SlipPaidOut := math.abs(ExitFill - ExitOrder) 1076 | // Net P&L including slippage and fees. 1077 | // TradePLX := (ExitFill-EntryFill)*(InLong[1]?1:-1)/entryStopGap 1078 | TradePLX := (ExitFill - EntryFill) * (InLong[1] ? 1 : -1) 1079 | 1080 | // Highest/LOwest PL (mult of X) reached during trade. 1081 | // TradePLXMax := max(TradePLX, TradePLXMax[1]) 1082 | // TradePLXMin := min(TradePLX, TradePLXMin[1]) 1083 | // Trade P&L in %. 1084 | // TradePLPct := TradePLX*EntryXPct 1085 | 1086 | 1087 | // Trade P&L in equity units. 1088 | // TradePLEqu := PositionIn*TradePLX*EntryXPct 1089 | TradePLEqu := PositionIn * TradePLX / EntryFill 1090 | 1091 | 1092 | // Add initial position size to P&L to get exit position before fees. 1093 | PositionOut := TradePLEqu + PositionIn 1094 | 1095 | // Convert slippage into equity units. 1096 | SlippageEqu := SlipPaidIn / EntryOrder * PositionIn + SlipPaidOut / ExitOrder * PositionOut 1097 | // Calculate a pseudo equity value given current trade result. 1098 | InTradeEquity := InTradeStartEquity + PositionOut - PositionIn 1099 | 1100 | // ————— Process active pyramided entries info. 1101 | if PyrEntries > 0 1102 | // ————— Update active pyramided entries informations. 1103 | // Add 1 in length for each pyr entry, but don't add if pyr entry made on this bar, as total already updated. 1104 | PyrTradeLengthsTot := PyrTradeLengthsTot + (PyramidEntry1stBar ? 0 : PyrEntries) 1105 | PyrTradeLengthsAvg := PyrTradeLengthsTot / PyrEntries 1106 | // Update following numbers for display purposes only, without worrying about fees. 1107 | PyrPositionOut := PyrPositionInQtyTot * ExitFill / _initialCapital 1108 | PyrTradePLEqu := (PyrPositionOut - PyrPositionInTot) * (InLong[1] ? 1 : -1) 1109 | PyrTradePLPct := PyrTradePLEqu / PyrPositionInTot 1110 | PyrTradePLX := PyrTradePLPct / PyrEntryXPctAvg 1111 | PyrTradePLX 1112 | 1113 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1114 | // ———————————————————————————————————————— 7. Pyramiding Rules ——————————————————————————————————————————————————————————— 1115 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1116 | // Here we evaluate the rules determining if pyramiding is allowed. 1117 | // ————— Pyramiding 1: Once after X multiple. 1118 | Pyramiding1Long = PyramidingType1 and Rclose > LastEntryPrice + entryStopGap * Pyramiding1MultX 1119 | Pyramiding1Short = PyramidingType1 and Rclose < LastEntryPrice - entryStopGap * Pyramiding1MultX 1120 | // ————— Pyramiding 2: Every % increment. 1121 | Pyramiding2Long = PyramidingType2 and Rclose > LastEntryPrice * (1 + Pyramiding2Pct / 100) 1122 | Pyramiding2Short = PyramidingType2 and Rclose < LastEntryPrice * (1 - Pyramiding2Pct / 100) 1123 | // ————— Pyramiding 3: Every price increment. 1124 | Pyramiding3Long = PyramidingType3 and Rclose > LastEntryPrice + Pyramiding3Val 1125 | Pyramiding3Short = PyramidingType3 and Rclose < LastEntryPrice - Pyramiding3Val 1126 | // ————— Pyramiding 4: Entry number=previous entry number. 1127 | Pyramiding4Long = PyramidingType4 and EntryNumber == LastEntryNumber 1128 | Pyramiding4Short = Pyramiding4Long 1129 | // ————— Pyramiding 5: Entry number<>previous entry number. 1130 | Pyramiding5Long = PyramidingType5 and EntryNumber != LastEntryNumber 1131 | Pyramiding5Short = Pyramiding5Long 1132 | // ————— Pyramiding 6: All opportunities. 1133 | Pyramiding6Long = PyramidingType6 1134 | Pyramiding6Short = PyramidingType6 1135 | 1136 | // ———————————————————— Assemble Pyramiding rules. 1137 | // In addition to the above rules allowing a pyramided entry, we need to be in a trade and the number of max pyramided entries must not have been reached. 1138 | PyramidLongOK = InLong[1] and InLong and PyrEntries < PyramidingMaxCnt and (PyramidingFilterNeeded ? FilterLongOK : true) and (Pyramiding1Long or Pyramiding2Long or Pyramiding3Long or Pyramiding4Long or Pyramiding5Long or Pyramiding6Long) 1139 | PyramidShortOK = InShort[1] and InShort and PyrEntries < PyramidingMaxCnt and (PyramidingFilterNeeded ? FilterShortOK : true) and (Pyramiding1Short or Pyramiding2Short or Pyramiding3Short or Pyramiding4Short or Pyramiding5Short or Pyramiding6Short) 1140 | 1141 | 1142 | 1143 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1144 | // ——————————————————————————————————————————— 8. In-trade stops —————————————————————————————————————————————————————————— 1145 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1146 | // We work in 2 steps: first we calculate the stop for each strat, 1147 | // then we evaluate the kick in conditions that will determine if the stop we have calculated will be used. 1148 | // In all cases a new stop will only replace a previous one if it closer to price, so this logic does not 1149 | // currently allow implementing stop strats where the stop moves against price. They won't cause errors; the 1150 | // stop will just never go further away from price than the previous one. 1151 | 1152 | // ———————————————————— In-trade stops 1153 | // ————— In-Trade Stop 1: Trailing stop using multiple of X% as max deviation 1154 | InTradeStop1Long = MaxReached - entryStopGap * InTradeStopType1Mult 1155 | InTradeStop1Short = MaxReached + entryStopGap * InTradeStopType1Mult 1156 | // ————— In-Trade Stop 2: Trailing stop using fixed percentage as max deviation 1157 | InTradeStop2Long = MaxReached * (1 - InTradeStopType2Pct / 100) 1158 | InTradeStop2Short = MaxReached * (1 + InTradeStopType2Pct / 100) 1159 | // ————— In-Trade Stop 3: Trailing stop using fixed value as max deviation 1160 | InTradeStop3Long = MaxReached - InTradeStopType3Val 1161 | InTradeStop3Short = MaxReached + InTradeStopType3Val 1162 | 1163 | // ————— In-Trade Stop 4: Move to Break Even. 1164 | InTradeStop4Long = EntryFill 1165 | InTradeStop4Short = EntryFill 1166 | 1167 | 1168 | // ————— In-Trade Stop 5: ATR stop 1169 | // Calculated using average of 2 preceeding MinOC/MaxOC and ATR gap. 1170 | InTradeStop5Atr = InTradeStopType5 ? ta.atr(InTradeStopType567Len) : na 1171 | InTradeStop5Long = InTradeStopType5 ? math.avg(MinOC[1], MinOC[2]) - InTradeStop5Atr * InTradeStopType567Mult : na 1172 | InTradeStop5Short = InTradeStopType5 ? math.avg(MaxOC[1], MaxOC[2]) + InTradeStop5Atr * InTradeStopType567Mult : na 1173 | 1174 | // ————— In-Trade Stop 8: Last Lo/Hi 1175 | InTradeStop8Long = low[1] 1176 | InTradeStop8Short = high[1] 1177 | 1178 | // ————— Select potential new stop. 1179 | InTradeStopLong_ = 0.0 1180 | InTradeStopShort_ = 0.0 1181 | InTradeStopLong_ := InTradeStopType1 ? InTradeStop1Long : InTradeStopType2 ? InTradeStop2Long : InTradeStopType3 ? InTradeStop3Long : InTradeStopType4 ? InTradeStop4Long : InTradeStopType5 ? InTradeStop5Long : InTradeStopType8 ? InTradeStop8Long : 0.0 1182 | InTradeStopShort_ := InTradeStopType1 ? InTradeStop1Short : InTradeStopType2 ? InTradeStop2Short : InTradeStopType3 ? InTradeStop3Short : InTradeStopType4 ? InTradeStop4Short : InTradeStopType5 ? InTradeStop5Short : InTradeStopType8 ? InTradeStop8Short : 0.0 1183 | 1184 | // Make sure stop is not on wrong side of the trade. 1185 | InTradeStopLong_ := InLong[1] and InTradeStopLong_ > Rhigh ? nz(InTradeStop[1]) : math.round_to_mintick(InTradeStopLong_) 1186 | InTradeStopShort_ := InShort[1] and InTradeStopShort_ < Rlow ? nz(InTradeStop[1]) : math.round_to_mintick(InTradeStopShort_) 1187 | 1188 | 1189 | // —————————— Kick In Conditions 1190 | PreviousStop = nz(InTradeStop[1]) 1191 | 1192 | // Determine if we can move stop or leave the entry stop behind and start using the in-trade stop. 1193 | // ————— Kisk In 1: When the stop passes the entry stop. 1194 | InTradeKick1Long = InTradeStopLong_ > EntryStop 1195 | InTradeKick1Short = InTradeStopShort_ < EntryStop 1196 | 1197 | // ————— Kisk In 2: When the stop passes Entry +/- X Multiple. 1198 | InTradeKick2Long_ = EntryFill + entryStopGap * InTradeKickType2Mult 1199 | InTradeKick2Short_ = EntryFill - entryStopGap * InTradeKickType2Mult 1200 | InTradeKick2Long = InTradeStopLong_ > math.max(math.round_to_mintick(InTradeKick2Long_), EntryStop) 1201 | InTradeKick2Short = InTradeStopShort_ < math.min(math.round_to_mintick(InTradeKick2Short_), EntryStop) 1202 | 1203 | // ————— Kisk In 3: When the stop passes Entry +/- %. 1204 | InTradeKick3Long_ = EntryFill * (1.0 + InTradeKickType3Pct / 100) 1205 | InTradeKick3Short_ = EntryFill * (1.0 - InTradeKickType3Pct / 100) 1206 | InTradeKick3Long = InTradeStopLong_ > math.max(math.round_to_mintick(InTradeKick3Long_), EntryStop) 1207 | InTradeKick3Short = InTradeStopShort_ < math.min(math.round_to_mintick(InTradeKick3Short_), EntryStop) 1208 | 1209 | // ————— Kisk In 4: When the stop passes Entry +/- Value. 1210 | InTradeKick4Long_ = EntryFill + InTradeKickType4Val 1211 | InTradeKick4Short_ = EntryFill - InTradeKickType4Val 1212 | InTradeKick4Long = InTradeStopLong_ > math.max(math.round_to_mintick(InTradeKick4Long_), EntryStop) 1213 | InTradeKick4Short = InTradeStopShort_ < math.min(math.round_to_mintick(InTradeKick4Short_), EntryStop) 1214 | 1215 | // ————— Kisk In 5: When the price passes Entry +/- % 1216 | InTradeKick5Long_ = EntryFill + InTradeKickType5Pct * EntryFill 1217 | InTradeKick5Short_ = EntryFill - InTradeKickType5Pct * EntryFill 1218 | InTradeKick5Long = MaxReached > math.max(math.round_to_mintick(InTradeKick5Long_), EntryStop) 1219 | InTradeKick5Short = MaxReached < math.min(math.round_to_mintick(InTradeKick5Short_), EntryStop) 1220 | 1221 | 1222 | // ————— Select Kick In 1223 | InTradeKickLong = InTradeKickType1 ? InTradeKick1Long : InTradeKickType2 ? InTradeKick2Long : InTradeKickType3 ? InTradeKick3Long : InTradeKickType4 ? InTradeKick4Long : InTradeKickType5 ? InTradeKick5Long : false 1224 | InTradeKickShort = InTradeKickType1 ? InTradeKick1Short : InTradeKickType2 ? InTradeKick2Short : InTradeKickType3 ? InTradeKick3Short : InTradeKickType4 ? InTradeKick4Short : InTradeKickType5 ? InTradeKick5Short : false 1225 | 1226 | // ———————————————————— Assemble In-Trade stop. 1227 | // Our potential new stop value is determined. Only use it as new one if it moved in trade direction compared to previous one. 1228 | // ————— Set In-trade stop for check on next bar. 1229 | // Exceptionally, on entry bar of a trade, init to EntryStop. 1230 | InTradeStop_ = FirstEntry1stBar ? EntryStop : InLong ? InTradeKickLong ? math.max(InTradeStopLong_, nz(InTradeStop[1])) : nz(InTradeStop[1]) : InTradeKickShort ? math.min(InTradeStopShort_, nz(InTradeStop[1])) : nz(InTradeStop[1]) 1231 | InTradeStop := math.max(0.0, InTradeStop_) 1232 | 1233 | 1234 | 1235 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1236 | // ———————————————————————————————————————————————— 9. Exits —————————————————————————————————————————————————————————————— 1237 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1238 | // 1239 | 1240 | // ----- Exit 0: Opposite Signal 1241 | Exit0Long = ExitType0 and EntryNumber < 0 1242 | Exit0Short = ExitType0 and EntryNumber > 0 1243 | 1244 | 1245 | // Exit 1: SROC Falling / Rising X 1246 | sroc = ta.ema(f_roc(), Exit1Smoothing) 1247 | srocFalling = ta.falling(sroc, Exit1Fall) 1248 | srocRising = ta.rising(sroc, Exit1Rise) 1249 | Exit1Long = ExitType1 and srocRising[1] and not srocRising 1250 | Exit1Short = ExitType1 and srocFalling[1] and not srocFalling 1251 | 1252 | 1253 | // ————— Exit 4: RSI Crosses 1254 | Exit4Rsi = ta.rsi(close, ExitType4Len) 1255 | Exit4Long = ExitType4 and ta.crossunder(Exit4Rsi, ExitType4LongLvl) 1256 | Exit4Short = ExitType4 and ta.crossover(Exit4Rsi, ExitType4ShortLvl) 1257 | 1258 | // ————— Exit 6: When the stop passes Entry +/- X Multiple. 1259 | Exit6Long_ = EntryFill + entryStopGap * ExitType6Mult 1260 | Exit6Short_ = EntryFill - entryStopGap * ExitType6Mult 1261 | Exit6TakeProfitLevel = InLong ? math.round_to_mintick(Exit6Long_) : InShort ? math.round_to_mintick(Exit6Short_) : 0.0 1262 | Exit6Long = ExitType6 and math.round_to_mintick(close) > Exit6TakeProfitLevel 1263 | Exit6Short = ExitType6 and math.round_to_mintick(close) < Exit6TakeProfitLevel 1264 | 1265 | // ————— Exit 7: When % gain is x amount (change) 1266 | Exit7Long_ = EntryFill + ExitType7Pct * EntryFill 1267 | Exit7Short_ = EntryFill - ExitType7Pct * EntryFill 1268 | Exit7TakeProfitLevel = InLong ? math.round_to_mintick(Exit7Long_) : InShort ? math.round_to_mintick(Exit7Short_) : 0.0 1269 | Exit7Long = ExitType7 and math.round_to_mintick(close) > Exit7TakeProfitLevel // Use close here because hard exits are calculated on close. 1270 | Exit7Short = ExitType7 and math.round_to_mintick(close) < Exit7TakeProfitLevel // So it's really a true TP Level. 1271 | 1272 | // ————— Exit 8: When % gain is x amount (equity) 1273 | Exit8LongShort = ExitType8 and InTradeEquity >= Equity + Equity * ExitType8Pct 1274 | 1275 | 1276 | // ———————————————————— Assemble exits 1277 | ExitLong = Exit0Long or Exit1Long or Exit4Long or Exit6Long or Exit7Long or Exit8LongShort 1278 | ExitShort = Exit0Short or Exit1Short or Exit4Short or Exit6Short or Exit7Short or Exit8LongShort 1279 | 1280 | // ———————————————————— Assemble reversals 1281 | reverse0Exit = i_rvrsePositionExit0 and ExitType0 1282 | reverse4Exit = i_rvrsePositionExit4 and ExitType4 1283 | 1284 | reversePosition = reverse0Exit or reverse4Exit 1285 | 1286 | 1287 | 1288 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1289 | // ——————————————————————————————————————— 10. Trade Entry/Exit Trigger Detection ————————————————————————————————————————— 1290 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1291 | // Detect Exit and then Entry trigger conditions for execution on next bar. 1292 | 1293 | // We start with exit triggers because if one is generated, we don't want to enter on the same bar. 1294 | // ————— Exit triggers 1295 | // Make an exception on Entry bar; since no previous trade stop existed, test current one. 1296 | StoppedCondition := not InTradeStopDisabled and (InLong and Rlow < InTradeStop[LongEntryTrigger[1] ? 0 : 1] or InShort and Rhigh > InTradeStop[ShortEntryTrigger[1] ? 0 : 1]) or InLong and Rlow <= EntryStop or InShort and Rhigh >= EntryStop 1297 | ExitTradeCondition = InLong and ExitLong or InShort and ExitShort 1298 | ExitCondition := StoppedCondition or ExitTradeCondition 1299 | LongExitTrigger = InLong and ExitCondition 1300 | ShortExitTrigger = InShort and ExitCondition 1301 | 1302 | 1303 | // ————— Entry triggers 1304 | // If a signal has triggered, this is its number (+ for long, - for short) 1305 | LongEntry = EntryNumber > 0 1306 | ShortEntry = EntryNumber < 0 1307 | // Identify Pyramiding entries so they can be treated distincly. 1308 | PyramidLongEntry = LongEntry and PyramidLongOK and not ExitCondition 1309 | PyramidShortEntry = ShortEntry and PyramidShortOK and not ExitCondition 1310 | // Confirm entries with filter and pyramiding. 1311 | LongEntryConfirmed = LongEntry and FilterLongOK and not InTrade or PyramidLongEntry and PyramidingOn 1312 | ShortEntryConfirmed = not LongEntryConfirmed and (ShortEntry and FilterShortOK and not InTrade or PyramidShortEntry and PyramidingOn) 1313 | // Determine if final conditions allow an entry trigger. The actual entry happens at the next bar. 1314 | // True only during the bar where an entry triggers. 1315 | // The 6 conditions are: 1316 | // 1. Trade direction allowed, 1317 | // 2. Confirmed signal, 1318 | // 3. Date filtering allows trade, 1319 | // 4. Enough bars have elapsed from beginning of dataset to have an ATR for entry stop, 1320 | // 5. Ruin has not occurred, 1321 | // 6. Circuit Breaker has not Triggered, 1322 | LongEntryTrigger := GenerateLongs and LongEntryConfirmed and TradeDateIsAllowed() and bar_index > EntryStopType2Len and not Ruin and not CircuitBreaker 1323 | ShortEntryTrigger := GenerateShorts and ShortEntryConfirmed and TradeDateIsAllowed() and bar_index > EntryStopType2Len and not Ruin and not CircuitBreaker 1324 | 1325 | 1326 | // ————— Assembly of states for ease in logical testing. 1327 | EntryTrigger := LongEntryTrigger or ShortEntryTrigger 1328 | ExitTrigger := LongExitTrigger or ShortExitTrigger and TradeDateIsAllowed() 1329 | PyramidEntry := PyramidLongEntry or PyramidShortEntry and TradeDateIsAllowed() 1330 | 1331 | 1332 | 1333 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1334 | // ————————————————————————————————————————— 11. Trade Exit Processing ———————————————————————————————————————————————————— 1335 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1336 | // On exit, all currently open positions (1st entry + pyramiding) are closed. 1337 | // Some of the currently valid trade data is needed in further modules, 1338 | // so we don't reset everything just yet. We will finish cleanup in next to last module before leaving the current bar. 1339 | if ExitTrigger[1] 1340 | // ——————————————— Trade state and numbers. 1341 | // As for entry, everything starts from the open of the bar following trigger, unless we were stopped out 1342 | ExitOrder := StoppedCondition[1] ? InTradeStop[1] : Ropen 1343 | // Calculate tentative slippage from Exit order level. 1344 | SlipPaidOut := SlippageType2 ? math.round_to_mintick(ExitOrder * SlippageType2OutPct / 100) : SlippageType3 ? math.round_to_mintick(SlippageType3OutVal * syminfo.mintick) : 0.0 1345 | // Calculate Exit Fill including slipppage, protecting against negative values. 1346 | ExitFill := math.max(0.0, ExitOrder + (InLong[1] ? -SlipPaidOut : SlipPaidOut)) 1347 | // Fill cannot be outside hi/lo. 1348 | ExitFill := InLong[1] ? math.max(Rlow[StoppedCondition[1] ? 1 : 0], ExitFill) : math.min(Rhigh[StoppedCondition[1] ? 1 : 0], ExitFill) 1349 | // Calculate slippage that actually occured (because it can be limited by bar's hi/lo). 1350 | SlipPaidOut := math.abs(ExitFill - ExitOrder) 1351 | // Net P&L after slippage. 1352 | // TradePLX := (ExitFill-EntryFill)*(InLong[1]?1:-1)/entryStopGap 1353 | TradePLX := (ExitFill - EntryFill) * (InLong[1] ? 1 : -1) 1354 | 1355 | // Highest/Lowest PL (mult of X) reached during trade. 1356 | // TradePLXMax := max(TradePLX, TradePLXMax[1]) 1357 | // TradePLXMin := min(TradePLX, TradePLXMin[1]) 1358 | // Trade P&L in %. 1359 | // TradePLPct := TradePLX*EntryXPct 1360 | // Trade P&L in equity units. 1361 | // TradePLEqu := PositionIn*TradePLX*EntryXPct 1362 | TradePLEqu := PositionIn * TradePLX / EntryFill 1363 | 1364 | // Add initial position size to P&L to get exit position before fees. 1365 | PositionOut := TradePLEqu + PositionIn 1366 | // % fees are calculated on exit's position size, so taking into account P&L after slippage. 1367 | FeesPaidOut := math.abs(FeesType2 ? PositionOut * FeesType2OutPct / 100 : 0.0) 1368 | // ————— From here we can integrate the impact of fees in our calcs. 1369 | // Subtract fees from previously calculated P&L (equ). 1370 | TradePLEqu := TradePLEqu - FeesPaidIn - FeesPaidOut 1371 | 1372 | // Recalculate PLX taking fees into account. 1373 | // TradePLX := TradePLEqu/EntryXEqu 1374 | // label.new(bar_index, high, text=tostring(TradePLX)) 1375 | TradePLX := TradePLEqu * EntryFill / PositionIn 1376 | // label.new(bar_index, low, text=tostring(TradePLX)) 1377 | 1378 | // Recalculate PL% taking fees into account. 1379 | // TradePLPct := TradePLX*EntryXPct 1380 | // Convert slippage in equ. 1381 | SlippageEqu := SlipPaidIn / EntryOrder * PositionIn + SlipPaidOut / ExitOrder * PositionOut 1382 | // Max Drawdown (from high to low) during trade. 1383 | TradeMaxDrawdown := math.min(0.0, (MaxReached[1] - MinReached[1]) * (InLong[1] ? -1 : 1) / MaxReached[1]) 1384 | 1385 | // ——————————————— Global 1st entry trade info. 1386 | First_Entries := First_Entries + 1 1387 | // Win/Lose state. 1388 | WinningTrade = TradePLX >= 0.0 1389 | LosingTrade = not WinningTrade 1390 | // P&L average=Expectancy=APPT in X. 1391 | First_PLXTot := First_PLXTot + TradePLX 1392 | First_PLXAvg := First_PLXTot / First_Entries 1393 | // X values: update avgs. 1394 | First_XTot := First_XTot + entryStopGap 1395 | First_XAvg := First_XTot / First_Entries 1396 | First_XPctTot := First_XPctTot + EntryXPct 1397 | First_XPctAvg := First_XPctTot / First_Entries 1398 | First_XEquTot := First_XEquTot + PositionIn * EntryXPct 1399 | First_XEquAvg := First_XEquTot / First_Entries 1400 | // Winning/Losing trade total X. 1401 | // First_WinsX := First_WinsX + (WinningTrade? TradePLX:0.0) 1402 | // First_LoseX := First_LoseX + (LosingTrade? TradePLX:0.0) 1403 | First_WinsEqu := First_WinsEqu + (WinningTrade ? PositionOut - PositionIn : 0.0) 1404 | First_LoseEqu := First_LoseEqu + (LosingTrade ? PositionOut - PositionIn : 0.0) 1405 | // label.new(bar_index, high, text=tostring(First_WinsX/_initialCapital)) 1406 | // label.new(bar_index, low, text=tostring(First_WinsEqu)) 1407 | // Winning/Losing trade totals. 1408 | First_Winning := First_Winning + OneZero(WinningTrade) 1409 | First_Losing := First_Losing + OneZero(LosingTrade) 1410 | // Other global trade data. 1411 | First_TradeLengthsTot := First_TradeLengthsTot + TradeLength 1412 | First_TradeLengthsAvg := First_TradeLengthsTot / First_Entries 1413 | First_WinsTL := First_WinsTL + (WinningTrade ? TradeLength : 0) 1414 | First_LoseTL := First_LoseTL + (LosingTrade ? TradeLength : 0) 1415 | // Entry slippage and fees were added on entry; only add exit ones here. 1416 | First_FeesEqu := First_FeesEqu + FeesPaidIn + FeesPaidOut 1417 | First_SlipEqu := First_SlipEqu + SlippageEqu 1418 | First_PositionInTot := First_PositionInTot + PositionIn 1419 | First_VolumeTraded := First_VolumeTraded + PositionIn + PositionOut 1420 | // Profit factor: gross wins / gross losses 1421 | First_ProfitFactor := First_LoseEqu == 0.0 ? 0.0 : math.abs(nz(First_WinsEqu / First_LoseEqu)) 1422 | 1423 | // If we have active pyramid entries, process them. 1424 | if PyrEntries > 0 1425 | // For Pyramided positions, instead of starting from the usual TradePLX, we will be working from the total of number 1426 | // of assets bought/sold at each successive entry, then calculating the delta between the currency value of the entries 1427 | // and the exit. PLX and PLPct will then be derived from there. 1428 | 1429 | // Exit position before fees. 1430 | PyrPositionOut := PyrPositionInQtyTot * ExitFill / _initialCapital 1431 | // Exit fees. 1432 | PyrFeesPaidOut := math.abs(FeesType2 ? PyrPositionOut * FeesType2OutPct / 100 : 0.0) 1433 | // Add exit fees to in fees (equ). 1434 | PyrFeesEquTot := PyrFeesEquTot + PyrFeesPaidOut 1435 | // Add slippage out (equ) (slippage is already in PyrSlipEquTot). 1436 | PyrSlipEquTot := PyrSlipEquTot + SlipPaidOut / ExitOrder * PyrPositionOut 1437 | // PL (equ) including Fees & Slippage. 1438 | PyrTradePLEqu := (PyrPositionOut - PyrPositionInTot) * (InLong[1] ? 1 : -1) - PyrFeesEquTot 1439 | // ————— From here we can integrate the impact of fees in PL. 1440 | // PL% taking fees into account. 1441 | PyrTradePLPct := PyrTradePLEqu / PyrPositionInTot 1442 | // PLX taking fees into account. 1443 | PyrTradePLX := PyrTradePLPct / PyrEntryXPctAvg 1444 | 1445 | // ——————————————— Global trade info. 1446 | // Total count of pyr entries (kept separate from First_Entries count). 1447 | Pyr_Entries := Pyr_Entries + PyrEntries 1448 | // Win/Lose state. 1449 | PyrWinningTrade = PyrTradePLX >= 0.0 1450 | PyrLosingTrade = not PyrWinningTrade 1451 | // Winning/Losing trade total (equ). 1452 | Pyr_WinsEqu := Pyr_WinsEqu + (PyrWinningTrade ? PyrTradePLEqu : 0.0) 1453 | Pyr_LoseEqu := Pyr_LoseEqu + (PyrLosingTrade ? PyrTradePLEqu : 0.0) 1454 | // Winning/Losing trade totals. 1455 | Pyr_Winning := Pyr_Winning + OneZero(PyrWinningTrade) * PyrEntries 1456 | Pyr_Losing := Pyr_Losing + OneZero(PyrLosingTrade) * PyrEntries 1457 | // P&L average per pyr entry. 1458 | Pyr_PLXTot := Pyr_PLXTot + PyrTradePLX * PyrEntries 1459 | Pyr_PLXAvg := Pyr_PLXTot / Pyr_Entries 1460 | // Other global trade data. 1461 | // Total volume traded. 1462 | Pyr_VolumeTraded := Pyr_VolumeTraded + PyrPositionOut + PyrPositionInTot 1463 | // Pyr Profit factor. 1464 | Pyr_ProfitFactor := Pyr_LoseEqu == 0.0 ? 0.0 : math.abs(nz(Pyr_WinsEqu / Pyr_LoseEqu)) 1465 | Pyr_ProfitFactor 1466 | 1467 | // ——————————————— Global combined (1st entries and pyramiding) numbers. 1468 | All_Entries := All_Entries + 1 + PyrEntries 1469 | // Win/Lose state. 1470 | WinningTrade := (TradePLX + PyrTradePLX * PyrEntries) / (PyrEntries + 1) >= 0.0 1471 | LosingTrade := not WinningTrade 1472 | // P&L average=Expectancy=APPT in X. 1473 | All_PLXTot := All_PLXTot + TradePLX + PyrTradePLX * PyrEntries 1474 | All_PLXAvg := All_PLXTot / All_Entries 1475 | // X values: update avgs. 1476 | All_XTot := All_XTot + entryStopGap + PyrEntryXAvg * PyrEntries 1477 | All_XAvg := All_XTot / All_Entries 1478 | All_XPctTot := All_XPctTot + EntryXPct + PyrEntryXPctAvg * PyrEntries 1479 | All_XPctAvg := All_XPctTot / All_Entries 1480 | All_XEquTot := All_XEquTot + PositionIn * EntryXPct + PyrPositionInTot * PyrEntryXPctAvg 1481 | All_XEquAvg := All_XEquTot / All_Entries 1482 | // Winning/Losing trade total X. 1483 | All_WinsX := All_WinsX + (WinningTrade ? TradePLX + PyrTradePLX * PyrEntries : 0.0) 1484 | All_LoseX := All_LoseX + (LosingTrade ? TradePLX + PyrTradePLX * PyrEntries : 0.0) 1485 | 1486 | All_WinsEqu := All_WinsEqu + (WinningTrade ? PositionOut - PositionIn + PyrTradePLEqu : 0.0) 1487 | All_LoseEqu := All_LoseEqu + (LosingTrade ? PositionOut - PositionIn + PyrTradePLEqu : 0.0) 1488 | // Winning/Losing trade totals. 1489 | All_Winning := All_Winning + OneZero(WinningTrade) + OneZero(WinningTrade) * PyrEntries 1490 | All_Losing := All_Losing + OneZero(LosingTrade) + OneZero(LosingTrade) * PyrEntries 1491 | // Other global trade data. 1492 | All_TradeLengthsTot := All_TradeLengthsTot + TradeLength + PyrTradeLengthsAvg * PyrEntries 1493 | All_TradeLengthsAvg := All_TradeLengthsTot / All_Entries 1494 | All_WinsTL := All_WinsTL + (WinningTrade ? TradeLength + PyrTradeLengthsAvg * PyrEntries : 0) 1495 | All_LoseTL := All_LoseTL + (LosingTrade ? TradeLength + PyrTradeLengthsAvg * PyrEntries : 0) 1496 | // Entry slippage and fees were added on entry; only add exit ones here. 1497 | All_FeesEqu := All_FeesEqu + FeesPaidIn + FeesPaidOut + PyrFeesEquTot 1498 | All_SlipEqu := All_SlipEqu + SlippageEqu + PyrSlipEquTot 1499 | All_PositionInTot := All_PositionInTot + PositionIn + PyrPositionInTot 1500 | All_VolumeTraded := All_VolumeTraded + PositionIn + PositionOut + PyrPositionOut + PyrPositionInTot 1501 | // Profit factor: gross wins / gross losses 1502 | All_ProfitFactor := All_LoseEqu == 0.0 ? 0.0 : math.abs(nz(All_WinsEqu / All_LoseEqu)) 1503 | 1504 | // ——————————————— Equity update. 1505 | // First entry and pyramiding (if any) exited; finish up a few numbers. 1506 | // Equity update with PL only (PositionIn never left equity) and subtracting fees. 1507 | Equity := Equity + PositionOut - PositionIn - FeesPaidIn - FeesPaidOut + PyrTradePLEqu 1508 | // Return on Capital. 1509 | ReturnOnEquity := Equity - 1.0 1510 | // Detect if capital is depleted. If so, new trades will no longer be opened. 1511 | Ruin := Equity <= 0 1512 | // Last update of in-trade equity before next trade. 1513 | InTradeEquity := Equity 1514 | InTradeEquity 1515 | 1516 | 1517 | 1518 | 1519 | //{ this only reverses positions on exits. 1520 | if ExitTrigger[1] and not StoppedCondition[1] and reversePosition and TradeDateIsAllowed() 1521 | LongEntryTrigger := not InLong[1] 1522 | ShortEntryTrigger := InLong[1] 1523 | EntryTrigger := true 1524 | EntryTrigger 1525 | //} 1526 | 1527 | 1528 | // ———————————————————— A.1. Global numbers 1529 | 1530 | // ————— A.1.1 Global combined (1st entries + pyramiding) 1531 | // APPTCur11 = nz((First_Winning/First_Entries*First_WinsEqu*_initialCapital/First_Winning)-(-First_Losing/First_Entries*First_LoseEqu*_initialCapital/First_Losing)) 1532 | EquityCu = Equity * _initialCapital // "ALL: Equity (curr)", "", color=AAA) // ————— Show 1533 | 1534 | 1535 | 1536 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1537 | // ———————————————————————————————————————————— 16. Variable resets ——————————————————————————————————————————————————————— 1538 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1539 | // ———————————————————— Last cleanup of exits. 1540 | // Now that we have used and plotted all Exit information, we can do final cleanup before next bar. 1541 | // Some of these values are reset a second time at the beginning of cycles; 1542 | // they are nonetheless reset here for cleaner between trade display purposes. 1543 | if ExitTrigger[1] 1544 | EntryFill := 0.0 1545 | EntryOrder := 0.0 1546 | entryStopGap := 0.0 1547 | EntryXPct := 0.0 1548 | EntryXEqu := 0.0 1549 | ExitOrder := 0.0 1550 | ExitFill := 0.0 1551 | FeesPaidIn := 0.0 1552 | FeesPaidOut := 0.0 1553 | InTradeEquity := Equity 1554 | InTradeStartEquity := Equity 1555 | LastEntryPrice := 0 1556 | PositionIn := 0.0 1557 | PositionOut := 0.0 1558 | SlippageEqu := 0.0 1559 | SlipPaidIn := 0.0 1560 | SlipPaidOut := 0.0 1561 | TakeProfitLevel := 0.0 1562 | TradeLength := 0 1563 | TradeMaxDrawdown := 0.0 1564 | 1565 | PyrEntries := 0 1566 | PyrEntryBarTot := 0 1567 | PyrEntryBarAvg := 0.0 1568 | PyrEntryFill := 0.0 1569 | PyrEntryFillTot := 0.0 1570 | PyrEntryFillAvg := 0.0 1571 | PyrEntryOrder := 0.0 1572 | PyrEntryX := 0.0 1573 | PyrEntryXAvg := 0.0 1574 | PyrEntryXPct := 0.0 1575 | PyrEntryXPctTot := 0.0 1576 | PyrEntryXPctAvg := 0.0 1577 | PyrEntryXTot := 0.0 1578 | PyrFeesPaidIn := 0.0 1579 | PyrFeesPaidOut := 0.0 1580 | PyrFeesEquTot := 0.0 1581 | PyrPositionIn := 0.0 1582 | PyrPositionOut := 0.0 1583 | PyrPositionInQtyTot := 0.0 1584 | PyrPositionInTot := 0.0 1585 | PyrSlipPaidIn := 0.0 1586 | PyrSlipPaidOut := 0.0 1587 | PyrSlipEquTot := 0.0 1588 | PyrTradePLEqu := 0.0 1589 | PyrTradePLX := 0.0 1590 | PyrTradeLengthsTot := 0 1591 | PyrTradeLengthsAvg := 0.0 1592 | PyrTradeLengthsAvg 1593 | 1594 | 1595 | 1596 | 1597 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1598 | // —————————————————————————————————————————— 17. Strategy() calls ———————————————————————————————————————————————————————— 1599 | // ———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— 1600 | int _signal = LongEntryTrigger ? 1 : ShortEntryTrigger ? -1 : LongExitTrigger ? 2 : ShortExitTrigger ? -2 : na 1601 | 1602 | 1603 | // Calculate Drawdown Metrics 1604 | [MaxDDPct, MaxDDPT, MaxDDDur] = f_getMaxDD(InTradeEquity, _initialCapital) 1605 | CircuitBreaker := -MaxDDPct >= i_RiskMaxDrawdownCB // Stops trades 1606 | 1607 | // Total Net Profit - Shows profitability and ROI 1608 | NetProfit = EquityCu - _initialCapital 1609 | 1610 | ROI = ReturnOnEquity * 100 1611 | 1612 | // Number of Trades - Shows # of trades taken during backtesting period 1613 | Trades = All_Winning + All_Losing 1614 | 1615 | // Number of Bars - Shows avg # of days in a trade 1616 | ATLen = f_barsToDays(All_TradeLengthsAvg) 1617 | 1618 | // Maximum Loss - Shows largest loss experienced on a per-trade basis. Normally, don’t want to exceed more than 1-2 % of equity. 1619 | var float MaxLoss = 0.0 1620 | MaxLoss := math.min(nz((Equity - Equity[1]) * _initialCapital, 0.0), MaxLoss) 1621 | 1622 | // Maximum Consecutive Losses (MCL) - The max consecutive losses endured throughout the backtesting period. Another important metric for trader psychology, this will help you understand how many losses you should be prepared to handle in order to achieve netprofit. 1623 | MaxConsecutiveLosses = f_getMCL(Equity, Trades, All_Losing) 1624 | 1625 | // Profit to Maximum Drawdown (P:MD) - A ratio for the Net Profit to the maximum drawdown. The higher the ratio is, the better. Large profits and small losses contribute to a good PMD This metric allows us to examine the profit with respect to risk. 1626 | PMD = NetProfit / math.abs(MaxDDPT) 1627 | 1628 | // Average Winning Trade, and Average Losing Trade = P&L total (equ) of all winning/losing trades / # Of winning/losing trades respectively. 1629 | All_AvgWin := All_WinsEqu / All_Winning 1630 | All_AvgLos := All_LoseEqu / All_Losing 1631 | 1632 | // Profit Loss Ratio (P:L) - Average profit over the average loss. Typically this number should be higher in trend following systems. Mean revision systems show lower values, but compensate with better win % 1633 | PLR = math.abs(All_AvgWin / All_AvgLos) 1634 | 1635 | // Percent Winners (% W) - The percentage of winning trades. Trend systems will usually have lower win percentages, since statistically the market is only trending 30% of the time. Mean reversions systems typically should have a high % W. 1636 | PctWinners = nz(100 * All_Winning / Trades) 1637 | 1638 | // Time Percentage (Time %) - The amount of time that the system has an open position. The more time you are in the market, the more you are exposed to the market's risk, not to mention you could be using that money for something else right? 1639 | TotalTradeTime = ta.cum(TradeDateIsAllowed() ? 1 : 0) 1640 | tradingTime = ta.cum(TradeDateIsAllowed() and InTrade ? 1 : 0) 1641 | // TimePct = round(100*(First_TradeLengthsTot+TradeLength) / TotalTradeTime, 2) 1642 | TimePct = 100 * tradingTime / TotalTradeTime 1643 | 1644 | 1645 | openProfit = TradePLEqu * _initialCapital // Unrealized gain/loss 1646 | _tradingDays = f_barsToDays(TotalTradeTime) // Days the strategy ran for 1647 | 1648 | 1649 | [_tradingDays, InTradeEquity, openProfit, NetProfit, ROI, Trades, ATLen, MaxDDPct, MaxDDPT, MaxLoss, MaxDDDur, MaxConsecutiveLosses, PMD, PLR, PctWinners, TimePct, _signal] 1650 | 1651 | 1652 | 1653 | 1654 | // ------ Display and Drawings 1655 | // Table Inputs 1656 | T_P1 = 'Bottom Center' 1657 | T_P2 = 'Bottom Left' 1658 | T_P3 = 'Bottom Right' 1659 | T_P4 = 'Middle Center' 1660 | T_P5 = 'Middle Left' 1661 | T_P6 = 'Middle Right' 1662 | T_P7 = 'Top Center' 1663 | T_P8 = 'Top Left' 1664 | T_P9 = 'Top Right' 1665 | i_t_position = input.string(T_P3, 'Table Position', options=[T_P1, T_P2, T_P3, T_P4, T_P5, T_P6, T_P7, T_P8, T_P9], group='Table') 1666 | t_position = i_t_position == T_P1 ? position.bottom_center : i_t_position == T_P2 ? position.bottom_left : i_t_position == T_P3 ? position.bottom_right : i_t_position == T_P4 ? position.middle_center : i_t_position == T_P5 ? position.middle_left : i_t_position == T_P6 ? position.middle_right : i_t_position == T_P7 ? position.top_center : i_t_position == T_P8 ? position.top_left : i_t_position == T_P9 ? position.top_right : na 1667 | 1668 | i_t_txtSze = input.string(defval='auto', title='Table Size', options=['auto', 'tiny', 'small', 'normal', 'large', 'huge'], group='Table') 1669 | t_headerClr = color.black 1670 | t_neuColor = color.gray 1671 | 1672 | t_rows = 7 1673 | t_columns = 15 1674 | 1675 | var table t_portfolioResults = table.new(t_position, t_columns, t_rows, border_width=3) // Create Global Table object 1676 | 1677 | 1678 | 1679 | f_getColor(_val, _max, _min) => 1680 | // _val: Value to be converted into color 1681 | // _max: Value that needs to be reached for Max Color 1682 | // _min: Value that needs to be reached for Min Color 1683 | // 1684 | // Returns: series[color] 1685 | color.from_gradient(_val, _min, _max, color.red, color.green) 1686 | 1687 | 1688 | f_fillCell(_table, _column, _row, _text, _bgcolor) => 1689 | // Fill the Table Cell 1690 | t_txtHA = text.align_center 1691 | t_txtVA = text.align_center 1692 | t_txtClr = color.white 1693 | table.cell(_table, _column, _row, _text, 0, 0, t_txtClr, t_txtHA, t_txtVA, i_t_txtSze, _bgcolor) 1694 | 1695 | 1696 | f_row(_initialCapital, _asset, _row) => 1697 | // Draw a Table Row 1698 | [_tradingDays, InTradeEquity, openProfit, NetProfit, ROI, NumTrades, AvgTradeLen, MaxDDPct, MaxDDPT, MaxLoss, MaxDDDur, MaxConsecutiveLosses, PMD, PLR, PctWinners, TimePct, Signal] = request.security(_asset, timeframe.period, f_getData(_initialCapital)) 1699 | 1700 | if _asset == syminfo.tickerid 1701 | if Signal == 1 1702 | label.new(bar_index, low, "Long Entry", yloc=yloc.belowbar, style=label.style_label_up, color=color.green) 1703 | else if Signal == 2 1704 | label.new(bar_index, high, "Long Exit", yloc=yloc.abovebar, style=label.style_label_down, color=color.yellow) 1705 | else if Signal == -1 1706 | label.new(bar_index, high, "Short Entry", yloc=yloc.abovebar, style=label.style_label_down, color=color.red) 1707 | else if Signal == -2 1708 | label.new(bar_index, low, "Short Exit", yloc=yloc.belowbar, style=label.style_label_up, color=color.yellow) 1709 | 1710 | // TEST 1711 | // [_tradingDays, InTradeEquity, openProfit, NetProfit, ROI,NumTrades,AvgTradeLen,MaxDDPct,MaxDDPT,MaxLoss,MaxDDDur,MaxConsecutiveLosses,PMD,PLR,PctWinners,TimePct,Signal] = f_getData(_initialCapital) 1712 | // TEST END 1713 | 1714 | // Alerts 1715 | if MaxDDPct >= i_RiskMaxDrawdown 1716 | alert(str.format('Max Drawdown Tolerance Exceeded for: {0} at {1, number, percent}', _asset, MaxDDPct / 100), alert.freq_all) 1717 | 1718 | if barstate.islast 1719 | f_fillCell(t_portfolioResults, 0, _row, _asset, t_headerClr) 1720 | f_fillCell(t_portfolioResults, 1, _row, str.tostring(NetProfit, '#.##'), f_getColor(NetProfit, _initialCapital, -_initialCapital)) 1721 | f_fillCell(t_portfolioResults, 2, _row, str.tostring(NumTrades, '#'), f_getColor(NumTrades, 200, 0)) 1722 | f_fillCell(t_portfolioResults, 3, _row, str.tostring(AvgTradeLen, '#.##'), t_neuColor) 1723 | f_fillCell(t_portfolioResults, 4, _row, str.tostring(MaxDDPT, '#.##'), f_getColor(MaxDDPct, 0, -i_RiskMaxDrawdown)) 1724 | f_fillCell(t_portfolioResults, 5, _row, str.tostring(MaxLoss, '#.##'), f_getColor(MaxLoss, 0, -(InTradeEquity * _initialCapital) * (2.0 / 100))) 1725 | f_fillCell(t_portfolioResults, 6, _row, str.tostring(MaxDDDur, '#'), t_neuColor) // TODO: This depends on Timeframe and Time horizon. (pass -MaxDDDur, and adjust length by timeframe.) 1726 | f_fillCell(t_portfolioResults, 7, _row, str.tostring(MaxConsecutiveLosses, '#'), f_getColor(-MaxConsecutiveLosses, 0, -11)) 1727 | f_fillCell(t_portfolioResults, 8, _row, str.tostring(PMD, '#.##'), f_getColor(PMD, 4, -4)) 1728 | f_fillCell(t_portfolioResults, 9, _row, str.tostring(PLR, '#.##'), f_getColor(PLR, 4, -4)) 1729 | f_fillCell(t_portfolioResults, 10, _row, str.tostring(PctWinners, format.percent), f_getColor(PctWinners, 100, 0)) 1730 | f_fillCell(t_portfolioResults, 11, _row, str.tostring(TimePct, format.percent), f_getColor(-TimePct, 0, -100)) 1731 | f_fillCell(t_portfolioResults, 12, _row, str.tostring(ROI, format.percent), f_getColor(ROI, 100, -100)) 1732 | f_fillCell(t_portfolioResults, 13, _row, str.tostring(openProfit, '#.##'), f_getColor(openProfit, NetProfit / 20, -NetProfit / 20)) 1733 | f_fillCell(t_portfolioResults, 14, _row, str.tostring(_tradingDays, '#.##'), t_neuColor) 1734 | 1735 | [nz(_tradingDays), nz(InTradeEquity), nz(openProfit), nz(NetProfit), nz(ROI), nz(NumTrades), nz(AvgTradeLen), nz(MaxDDPct), nz(MaxDDPT), nz(MaxLoss), nz(MaxDDDur), nz(MaxConsecutiveLosses), nz(PMD), nz(PLR), nz(PctWinners), nz(TimePct), Signal] 1736 | 1737 | 1738 | 1739 | 1740 | f_table() => 1741 | // Draw the Table. 1742 | _i_sym1 = syminfo.tickerid 1743 | _i_sym2 = input.symbol(defval='FX_IDC:CADJPY', title='Symbol 2', group='Portfolio Selection') 1744 | _i_sym3 = input.symbol(defval='FX_IDC:GBPUSD', title='Symbol 3', group='Portfolio Selection') 1745 | _i_sym4 = input.symbol(defval='TVC:USOIL', title='Symbol 4', group='Portfolio Selection') 1746 | _i_sym5 = input.symbol(defval='BATS:BAC', title='Symbol 5', group='Portfolio Selection') 1747 | 1748 | // Draw Headers 1749 | i_fullText = input.bool(true, 'Show Full Header Text', group='Table') 1750 | if barstate.islast 1751 | f_fillCell(t_portfolioResults, 0, 0, 'Portfolio', t_headerClr) 1752 | f_fillCell(t_portfolioResults, 1, 0, i_fullText ? 'Net\nProfit' : 'NP', t_headerClr) 1753 | f_fillCell(t_portfolioResults, 2, 0, i_fullText ? '# of\nTrades' : '#T', t_headerClr) 1754 | f_fillCell(t_portfolioResults, 3, 0, i_fullText ? 'Average\nTrade\nLength' : 'ATL', t_headerClr) 1755 | f_fillCell(t_portfolioResults, 4, 0, i_fullText ? 'Max\nDrawdown' : 'MD', t_headerClr) 1756 | f_fillCell(t_portfolioResults, 5, 0, i_fullText ? 'Max\nLoss' : 'ML', t_headerClr) 1757 | f_fillCell(t_portfolioResults, 6, 0, i_fullText ? 'Max\nDrawdown\nDuration' : 'MDD', t_headerClr) 1758 | f_fillCell(t_portfolioResults, 7, 0, i_fullText ? 'Max\nConsecutive\nLosing\nTrades' : 'MCL', t_headerClr) 1759 | f_fillCell(t_portfolioResults, 8, 0, i_fullText ? 'Profit\n—————\nMax\nDrawdown' : 'P:MD', t_headerClr) 1760 | f_fillCell(t_portfolioResults, 9, 0, i_fullText ? 'Profit\n—————\nLoss' : 'P:L', t_headerClr) 1761 | f_fillCell(t_portfolioResults, 10, 0, i_fullText ? '%\nWinning\nTrades' : '%W', t_headerClr) 1762 | f_fillCell(t_portfolioResults, 11, 0, i_fullText ? '%\nTime\nin\nMarket' : '%T', t_headerClr) 1763 | f_fillCell(t_portfolioResults, 12, 0, i_fullText ? 'Return\nOn\nInvestment' : 'ROI', t_headerClr) 1764 | f_fillCell(t_portfolioResults, 13, 0, i_fullText ? 'Open\nProfit' : 'OP', t_headerClr) 1765 | f_fillCell(t_portfolioResults, 14, 0, i_fullText ? 'Trading\nDays' : 'TD', t_headerClr) 1766 | 1767 | 1768 | 1769 | // Draw Rows 1770 | numberOfAssetsInPortfolio = 5 1771 | portfolioAllocation = PosTypeCapital / numberOfAssetsInPortfolio 1772 | [TradingDays1, ITE1, openProfit1, NetProfit1, ROI1, NumTrades1, AvgTradeLen1, MaxDDPct1, MaxDDPT1, MaxLoss1, MaxDDDur1, MaxConsecutiveLosses1, PMD1, PLR1, PctWinners1, TimePct1, Signal1] = f_row(portfolioAllocation, _i_sym1, 1) 1773 | [TradingDays2, ITE2, openProfit2, NetProfit2, ROI2, NumTrades2, AvgTradeLen2, MaxDDPct2, MaxDDPT2, MaxLoss2, MaxDDDur2, MaxConsecutiveLosses2, PMD2, PLR2, PctWinners2, TimePct2, Signal2] = f_row(portfolioAllocation, _i_sym2, 2) 1774 | [TradingDays3, ITE3, openProfit3, NetProfit3, ROI3, NumTrades3, AvgTradeLen3, MaxDDPct3, MaxDDPT3, MaxLoss3, MaxDDDur3, MaxConsecutiveLosses3, PMD3, PLR3, PctWinners3, TimePct3, Signal3] = f_row(portfolioAllocation, _i_sym3, 3) 1775 | [TradingDays4, ITE4, openProfit4, NetProfit4, ROI4, NumTrades4, AvgTradeLen4, MaxDDPct4, MaxDDPT4, MaxLoss4, MaxDDDur4, MaxConsecutiveLosses4, PMD4, PLR4, PctWinners4, TimePct4, Signal4] = f_row(portfolioAllocation, _i_sym4, 4) 1776 | [TradingDays5, ITE5, openProfit5, NetProfit5, ROI5, NumTrades5, AvgTradeLen5, MaxDDPct5, MaxDDPT5, MaxLoss5, MaxDDDur5, MaxConsecutiveLosses5, PMD5, PLR5, PctWinners5, TimePct5, Signal5] = f_row(portfolioAllocation, _i_sym5, 5) 1777 | // [TradingDays6, ITE6, NetProfit6,ROI6,NumTrades6,AvgTradeLen6,MaxDDPct6,MaxDDPT6,MaxLoss6,MaxDDDur6,MaxConsecutiveLosses6,PMD6,PLR6,PctWinners6,TimePct6,Signal6] = f_row("XRPUSD", 7, _i_size,_i_textAlign) 1778 | 1779 | // Track Totals 1780 | maxTD = math.max(TradingDays1, TradingDays2, TradingDays3, TradingDays4, TradingDays5) 1781 | totalITE = math.avg(ITE1, ITE2, ITE3, ITE4, ITE5) 1782 | totalOP = openProfit1 + openProfit2 + openProfit3 + openProfit4 + openProfit5 1783 | totalNetProfit = NetProfit1 + NetProfit2 + NetProfit3 + NetProfit4 + NetProfit5 1784 | totalROI = totalNetProfit / PosTypeCapital * 100 1785 | totalTrades = NumTrades1 + NumTrades2 + NumTrades3 + NumTrades4 + NumTrades5 1786 | [totalMaxDDPct, totalMaxDDPT, totalMaxDDDur] = f_getMaxDD(totalITE, PosTypeCapital) 1787 | 1788 | NetChange = ta.change(totalNetProfit) 1789 | totalMaxLoss = 0. 1790 | totalMaxLoss := math.min(NetChange, nz(totalMaxLoss[1])) 1791 | var totalMCLCount = 0 1792 | totalMCLCount := NetChange < 0 ? totalMCLCount + 1 : 0 1793 | var totalMCL = 0 1794 | totalMCL := math.max(totalMCLCount, totalMCL) 1795 | totalPMD = totalNetProfit / math.abs(totalMaxDDPT) 1796 | totalPLR = math.avg(PLR1, PLR2, PLR3, PLR4, PLR5) 1797 | totalPW = (NumTrades1 * PctWinners1 / 100 + NumTrades2 * PctWinners2 / 100 + NumTrades3 * PctWinners3 / 100 + NumTrades4 * PctWinners4 / 100 + NumTrades5 * PctWinners5 / 100) / totalTrades * 100 1798 | 1799 | InTrade = TradeDateIsAllowed() and (ta.change(totalITE) ? true : false) 1800 | TotalTradeBars = ta.cum(TradeDateIsAllowed() ? 1 : 0) 1801 | TotalInTradeBars = ta.cum(InTrade ? 1 : 0) 1802 | totalATL = f_barsToDays(TotalInTradeBars / totalTrades) 1803 | totalTP = TotalInTradeBars / TotalTradeBars * 100 1804 | totalSignal = Signal1 1805 | 1806 | 1807 | 1808 | 1809 | _totalRow = numberOfAssetsInPortfolio + 1 1810 | if barstate.islast 1811 | // Draw Totals 1812 | f_fillCell(t_portfolioResults, 0, _totalRow, 'Total', t_headerClr) 1813 | f_fillCell(t_portfolioResults, 1, _totalRow, str.tostring(totalNetProfit, '#.##'), f_getColor(totalNetProfit, PosTypeCapital, -PosTypeCapital)) 1814 | f_fillCell(t_portfolioResults, 2, _totalRow, str.tostring(totalTrades, '#'), f_getColor(totalTrades, 200, 0)) 1815 | f_fillCell(t_portfolioResults, 3, _totalRow, str.tostring(totalATL, '#.##'), t_neuColor) 1816 | f_fillCell(t_portfolioResults, 4, _totalRow, str.tostring(totalMaxDDPT, '#.##'), f_getColor(totalMaxDDPT, 0, -totalITE * PosTypeCapital * (i_RiskMaxDrawdown / 100))) 1817 | f_fillCell(t_portfolioResults, 5, _totalRow, str.tostring(totalMaxLoss, '#.##'), f_getColor(totalMaxLoss, 0, -totalITE * PosTypeCapital * (2.0 / 100))) 1818 | f_fillCell(t_portfolioResults, 6, _totalRow, str.tostring(totalMaxDDDur, '#'), t_neuColor) 1819 | f_fillCell(t_portfolioResults, 7, _totalRow, str.tostring(totalMCL, '#'), f_getColor(-totalMCL, 0, -11)) 1820 | f_fillCell(t_portfolioResults, 8, _totalRow, str.tostring(totalPMD, '#.##'), f_getColor(totalPMD, 4, -4)) 1821 | f_fillCell(t_portfolioResults, 9, _totalRow, str.tostring(totalPLR, '#.##'), f_getColor(totalPLR, 4, -4)) 1822 | f_fillCell(t_portfolioResults, 10, _totalRow, str.tostring(totalPW, format.percent), f_getColor(totalPW, 100, 0)) 1823 | f_fillCell(t_portfolioResults, 11, _totalRow, str.tostring(totalTP, format.percent), f_getColor(-totalTP, 0, -100)) 1824 | f_fillCell(t_portfolioResults, 12, _totalRow, str.tostring(totalROI, format.percent), f_getColor(totalROI, 100, -100)) 1825 | f_fillCell(t_portfolioResults, 13, _totalRow, str.tostring(totalOP, '#.##'), f_getColor(totalOP, totalNetProfit / 20, -totalNetProfit / 20)) 1826 | f_fillCell(t_portfolioResults, 14, _totalRow, str.tostring(maxTD, '#.##'), t_neuColor) 1827 | 1828 | f_table() -------------------------------------------------------------------------------- /renko-acc.pine: -------------------------------------------------------------------------------- 1 | //@version=4 2 | // Renko Acceleration 3 | // 4 | // Renko is a very useful charting method for analyzing stock movement. 5 | // It does a great job of filtering out all the excess noise so that all we're 6 | // left with is pure price action. But, what about time? Time is a fundamental 7 | // part of chart analysis and without it we're only seeing part of the 8 | // picture. After all, shouldn't we take a very different approach to trading 9 | // signals from a block that renders in 2 seconds as opposed to a block that 10 | // renders in 30 minutes? 11 | // 12 | // This indicator provides the best of both worlds, enabling us to correlate 13 | // the passing of time with price movement and see clearly when squeezes and 14 | // breakouts occur. 15 | // 16 | // As the indicator turns up to green we can see that volatility is on the 17 | // move and the market is accelerating (breakout), and as it turns down to red 18 | // the market is stagnating (squeeze). There is also an alternate 'Precise' 19 | // view which renders the exact time per block for more granular analysis. 20 | // 21 | study("Renko Acceleration", overlay=false) 22 | 23 | // Allow selection of signal content. 24 | IncludeFilter = input(true, 'Include Filter') 25 | 26 | mode = input("Momentum", "Display Mode", options=["Momentum", "Precise"]) 27 | smoothing = input(10, "Smoothing", input.integer, minval=0) 28 | 29 | float duration = (time-time[1]) / (1000*60) // block time in seconds 30 | float accel = na 31 | color accelColor = na 32 | 33 | if mode == "Momentum" 34 | bline = sma(duration, 100) * 1.25 35 | accel := max(bline-duration, -20) // trim negative outliers 36 | accel := smoothing ? sma(accel, smoothing) : accel 37 | accelColor := accel > 15 ? color.lime : 38 | accel > 5 ? color.green : 39 | accel > 0 ? color.olive : 40 | accel > -5 ? color.yellow : 41 | accel > -10 ? color.orange : color.red 42 | 43 | if mode == "Precise" 44 | accel := duration 45 | accelColor := duration > 30 ? color.red : 46 | duration > 15 ? color.orange : 47 | duration > 5 ? color.yellow : color.silver 48 | 49 | plot(accel, "Time", style=plot.style_histogram, color=accelColor) 50 | 51 | // ————— Filter. 52 | FilterLong = accel > 0 53 | FilterShort = accel > 0 54 | 55 | // ————— Build required values corresponding to states. 56 | Filter = FilterLong ? 0.001 : FilterShort ? 0.001 : 0 57 | 58 | // ————— We must decide which value will be sent in case more than one is different than 0, since only one value can be sent at each bar. 59 | // Priority is given to exits, with filter states coming last. 60 | Signal = IncludeFilter and Filter != -0.001 ? Filter : na 61 | // ————— Plot signal which is to be connected to the Engine through the External Indicator field at the very bottom of the Engine's Settings/Inputs. 62 | plot(Signal, 'Signal') -------------------------------------------------------------------------------- /signal-ext/ao-signal.pine: -------------------------------------------------------------------------------- 1 | //@version=5 2 | //@author=LucF, for PineCoders 3 | 4 | // Signal for Backtesting-Trading Engine [PineCoders] 5 | // v1.3, 2019.06.21 02:58 6 | 7 | // PineCoders, Tools and ideas for all Pine coders. 8 | // This script is referenced from the PineCoders FAQ & Code here: http://www.pinecoders.com/faq_and_code/#how-can-i-use-one-scripts-output-as-an-input-into-another 9 | // Documentation: https://www.tradingview.com/script/y4CvTwRo-Signal-for-Backtesting-Trading-Engine-PineCoders/ 10 | 11 | // This script is an example of how to configure a signal for the PineCoders Backtesting-Trading Engine 12 | // which you will find here: https://www.tradingview.com/script/dYqL95JB-Backtesting-Trading-Engine-PineCoders/ 13 | // We used the built-in MACD indicator as a template. The script's signal adheres to the following protocol: 14 | 15 | // EXTERNAL SIGNAL PROTOCOL 16 | // Only one external indicator can be connected to a script; in order to leverage its use to the fullest, 17 | // the engine provides options to use it as either and entry signal, an entry/exit signal or a filter. 18 | // When used as an entry signal, you can also use the signal to provide the entry stop. Here’s how this works. 19 | // - For filter state: supply +1.0 for bull (long entries allowed), -1.0 for bear (short entries allowed). 20 | // - For entry signals: supply +2.0 for long, -2.0 for short. 21 | // - For exit signals: supply +3.0 for exit from long, -3.0 for exit from short. 22 | // - To send an entry stop level with entry signal: Send positive stop level for long entry 23 | // (e.g. 103.33 to enter a long with a stop at 103.33), negative stop level for short entry 24 | // (e.g. -103.33 to enter a short with a stop at 103.33). If you use this feature, 25 | // your indicator will have to check for exact stop levels of 1.0, 2.0 or 3.0 and their negative counterparts, 26 | // and fudge them with a tick in order to avoid confusion with other signals in the protocol. 27 | // Note that each use must be confirmed in the corresponding section of the script's Settings/Inputs. 28 | 29 | // CONFIGURATION OF ALERTS 30 | // Entry and Event alerts will generally be configured to trigger Once Per Bar Close. 31 | // Exit alerts can be configured to trigger Once Per Bar Close or Once Per Bar, depending on how fast 32 | // you want them. Once Per Bar means the alert will trigger the moment the condition is first encountered 33 | // during the realtime bar, instead of at the bar's close. 34 | // Entry and Exit alerts trigger at the same bar where the relevant marker will appear on the chart. 35 | // Event alerts trigger on the bar following the appearance of their respective marker on the chart. 36 | 37 | indicator('Signal for Backtesting-Trading Engine [PineCoders]', timeframe="1") 38 | 39 | // Allow selection of signal content. 40 | IncludeFilter = input(true, 'Include Filter') 41 | IncludeEntries = input(true, 'Include Entries') 42 | IncludeStops = input(true, 'Include Stops with Entries') 43 | IncludeExits = input(true, 'Include Exits') 44 | 45 | 46 | // —————————————————————————————————————————————————————————————————————————————————————— 47 | // ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ 48 | // This part is the section where you would put your own indicator and define conditions. 49 | 50 | // ————— TV built-in MACD code. 51 | ao = ta.sma(close,5) - ta.sma(close,34) 52 | diff = ao - ao[1] 53 | plot(ao, color = diff <= 0 ? #F44336 : #009688, style=plot.style_columns) 54 | 55 | 56 | // ————— Filter. 57 | FilterLong = ao > 0 58 | FilterShort = ao < 0 59 | 60 | // ————— Entries. 61 | EnterLong = ta.crossover(diff, 0) 62 | EnterShort = ta.crossunder(diff, 0) 63 | 64 | // ————— Stops. 65 | Atr = ta.atr(14) 66 | StopLong = math.min(ta.lowest(5), math.min(close, open) - Atr * 1.5) 67 | StopShort = math.max(ta.highest(5), math.max(close, open) + Atr * 1.5) 68 | 69 | // ————— Exits. 70 | ExitLong = ta.crossunder(diff, ao) and diff > 0 71 | ExitShort = ta.crossover(diff, ao) and diff < 0 72 | 73 | // ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ 74 | // —————————————————————————————————————————————————————————————————————————————————————— 75 | 76 | 77 | // ————— This function ensures that no stop value equal to one of the protocol's reserved values is sent, so it isn't misinterpreted as a signal. 78 | // The fudge is in the direction where the trade's risk is decreased by a tick, so added for longs and subtracted for shorts. 79 | FudgeStop(_stop, _fudgedir) => 80 | _stop == 1.0 or _stop == 2.0 or _stop == 3.0 ? _stop + syminfo.mintick * _fudgedir : _stop 81 | 82 | // ————— Build required values corresponding to states. 83 | Filter = FilterLong ? 1 : FilterShort ? -1 : 0 84 | Entries = EnterLong ? 2 : EnterShort ? -2 : 0 85 | FudgeStop__1 = FudgeStop(StopLong, 1) 86 | FudgeStop__2 = FudgeStop(StopShort, -1) 87 | Stops = EnterLong ? FudgeStop__1 : EnterShort ? -FudgeStop__2 : 0 88 | Exits = ExitLong ? 3 : ExitShort ? -3 : 0 89 | 90 | // ————— We must decide which value will be sent in case more than one is different than 0, since only one value can be sent at each bar. 91 | // Priority is given to exits, with filter states coming last. 92 | Signal = IncludeExits and Exits != 0 ? Exits : IncludeStops and Stops != 0 ? Stops : IncludeEntries and Entries != 0 ? Entries : IncludeFilter and Filter != 0 ? Filter : na 93 | 94 | // ————— Plot signal which is to be connected to the Engine through the External Indicator field at the very bottom of the Engine's Settings/Inputs. 95 | plot(Signal, 'Signal') 96 | 97 | 98 | // ————— Plots markers. 99 | plotshape(IncludeEntries and EnterLong, 'Enter Long', style=shape.triangleup, location=location.bottom, color=color.new(color.green, 0), size=size.small, text='Enter\nLong') 100 | plotshape(IncludeEntries and EnterShort, 'Enter Short', style=shape.triangledown, location=location.top, color=color.new(color.red, 0), size=size.small, text='Enter\nShort') 101 | plotshape(IncludeExits and ExitLong, 'Exit Long', style=shape.triangledown, location=location.top, color=color.new(color.green, 0), size=size.tiny, text='Exit\nLong') 102 | plotshape(IncludeExits and ExitShort, 'Exit Short', style=shape.triangleup, location=location.bottom, color=color.new(color.red, 0), size=size.tiny, text='Exit\nShort') 103 | 104 | bgcolor(color=IncludeFilter and FilterLong ? color.lime : IncludeFilter and FilterShort ? color.red : na, title='Filter Background', transp=90) 105 | 106 | -------------------------------------------------------------------------------- /signal-ext/fg-cycle-wip.pine: -------------------------------------------------------------------------------- 1 | //@version=5 2 | //@author=LucF, for PineCoders 3 | 4 | // Signal for Backtesting-Trading Engine [PineCoders] 5 | // v1.3, 2019.06.21 02:58 6 | 7 | // PineCoders, Tools and ideas for all Pine coders. 8 | // This script is referenced from the PineCoders FAQ & Code here: http://www.pinecoders.com/faq_and_code/#how-can-i-use-one-scripts-output-as-an-input-into-another 9 | // Documentation: https://www.tradingview.com/script/y4CvTwRo-Signal-for-Backtesting-Trading-Engine-PineCoders/ 10 | 11 | // This script is an example of how to configure a signal for the PineCoders Backtesting-Trading Engine 12 | // which you will find here: https://www.tradingview.com/script/dYqL95JB-Backtesting-Trading-Engine-PineCoders/ 13 | // We used the built-in MACD indicator as a template. The script's signal adheres to the following protocol: 14 | 15 | // EXTERNAL SIGNAL PROTOCOL 16 | // Only one external indicator can be connected to a script; in order to leverage its use to the fullest, 17 | // the engine provides options to use it as either and entry signal, an entry/exit signal or a filter. 18 | // When used as an entry signal, you can also use the signal to provide the entry stop. Here’s how this works. 19 | // - For filter state: supply +1.0 for bull (long entries allowed), -1.0 for bear (short entries allowed). 20 | // - For entry signals: supply +2.0 for long, -2.0 for short. 21 | // - For exit signals: supply +3.0 for exit from long, -3.0 for exit from short. 22 | // - To send an entry stop level with entry signal: Send positive stop level for long entry 23 | // (e.g. 103.33 to enter a long with a stop at 103.33), negative stop level for short entry 24 | // (e.g. -103.33 to enter a short with a stop at 103.33). If you use this feature, 25 | // your indicator will have to check for exact stop levels of 1.0, 2.0 or 3.0 and their negative counterparts, 26 | // and fudge them with a tick in order to avoid confusion with other signals in the protocol. 27 | // Note that each use must be confirmed in the corresponding section of the script's Settings/Inputs. 28 | 29 | // CONFIGURATION OF ALERTS 30 | // Entry and Event alerts will generally be configured to trigger Once Per Bar Close. 31 | // Exit alerts can be configured to trigger Once Per Bar Close or Once Per Bar, depending on how fast 32 | // you want them. Once Per Bar means the alert will trigger the moment the condition is first encountered 33 | // during the realtime bar, instead of at the bar's close. 34 | // Entry and Exit alerts trigger at the same bar where the relevant marker will appear on the chart. 35 | // Event alerts trigger on the bar following the appearance of their respective marker on the chart. 36 | 37 | // ══════════════════════════════════════════════════════════════════════════════════════════════════ // 38 | //# * ══════════════════════════════════════════════════════════════════════════════════════════════ 39 | //# * 40 | //# * Study : Trading Psychology - Fear & Greed Index by DGT 41 | //# * Author : © dgtrd 42 | //# * 43 | //# * Revision History 44 | //# * Release : Jul 21, 2020 45 | //# * Update : Apr 09, 2021 : Added ability to display Current Time Frame's Fear and Greed Index 46 | //# * Update : Apr 09, 2022 : Added Price Candle display option with Fear & Greed weighted risk levels 47 | //# * 48 | //# * ══════════════════════════════════════════════════════════════════════════════════════════════ 49 | // ══════════════════════════════════════════════════════════════════════════════════════════════════ // 50 | indicator('F&G CYCLE ʙʏ DGT Signal for Backtesting-Trading Engine [PineCoders]') 51 | 52 | // Allow selection of signal content. 53 | IncludeFilter = input(true, 'Include Filter') 54 | IncludeEntries = input(true, 'Include Entries') 55 | IncludeStops = input(true, 'Include Stops with Entries') 56 | IncludeExits = input(true, 'Include Exits') 57 | 58 | 59 | // -Inputs ====================================================================================== // 60 | 61 | display = input.string('Fear & Greed Index', 'Display as', options=['Price Candles', 'Fear & Greed Index']) 62 | smoothLength = input.int(5, 'RMA Smoothing Length', minval=1, maxval=13) 63 | faster = input(false, 'Fear & Greed Index : Calculation with Faster Legths') 64 | daily = input(false, 'Display Daily Time Frame Calculated Value') 65 | lagendDesc = input(true, 'Show Color Legend') 66 | hLabel = input(false, 'Hide Statistical Label') 67 | 68 | 69 | fastLength = faster ? 13 : 21 70 | slowLength = faster ? 89 : 144 71 | 72 | // -Calculations ================================================================================ // 73 | 74 | pmacd = (close / ta.ema(close, slowLength) - 1) * 100 75 | pmacd_d = request.security(syminfo.tickerid, 'D', pmacd, barmerge.gaps_off, barmerge.lookahead_on) 76 | 77 | ror = (close - close[slowLength]) / close[slowLength] * 100 78 | ror_d = request.security(syminfo.tickerid, 'D', ror, barmerge.gaps_off, barmerge.lookahead_on) 79 | 80 | accDist = close == high and close == low or high == low ? 0 : (2 * close - low - high) / (high - low) 81 | nzVolume = nz(volume) 82 | moneyFlow = math.sum(accDist * nzVolume, fastLength) / math.sum(nzVolume, fastLength) * 100 83 | 84 | accDist_d = request.security(syminfo.tickerid, 'D', accDist, barmerge.gaps_off, barmerge.lookahead_on) 85 | nzVolume_d = request.security(syminfo.tickerid, 'D', nzVolume) 86 | moneyFlow_d = math.sum(accDist_d * nzVolume_d, fastLength) / math.sum(nzVolume_d, fastLength) * 100 87 | 88 | vix = request.security('VIX', timeframe.period, -(close / ta.ema(close, slowLength) - 1) * 100, barmerge.gaps_off, barmerge.lookahead_on) 89 | vix_d = request.security('VIX', 'D', -(close / ta.ema(close, slowLength) - 1) * 100, barmerge.gaps_off, barmerge.lookahead_on) 90 | 91 | gold = request.security('GOLD', timeframe.period, -(1 - close[fastLength] / close) * 100, barmerge.gaps_off, barmerge.lookahead_on) 92 | gold_d = request.security('GOLD', 'D', -(1 - close[fastLength] / close) * 100, barmerge.gaps_off, barmerge.lookahead_on) 93 | 94 | cycle_raw = nzVolume ? math.avg(pmacd, ror, moneyFlow, vix, gold) : math.avg(pmacd, ror, vix, gold) 95 | cycle_raw_d = nzVolume_d ? math.avg(pmacd_d, ror_d, moneyFlow_d, vix_d, gold_d) : math.avg(pmacd_d, ror_d, vix_d, gold_d) 96 | 97 | cycle = ta.rma(cycle_raw, smoothLength) 98 | cycle_d = ta.rma(cycle_raw_d, smoothLength) 99 | 100 | f_getText(_cycle) => 101 | if _cycle > 73 102 | 'Extreame Greed' 103 | else if _cycle > 33 104 | 'Greed' 105 | else if _cycle < -15 106 | 'Fear' 107 | else if _cycle < -41 108 | 'Extreame Fear' 109 | else 110 | 'Neutral' 111 | 112 | f_getColor(_cycle) => 113 | if _cycle >= 73 114 | #006400 115 | else if _cycle >= 63 and _cycle < 73 116 | #007439 117 | else if _cycle >= 52 and _cycle < 63 118 | #008368 119 | else if _cycle >= 41 and _cycle < 52 120 | #009096 121 | else if _cycle >= 30 and _cycle < 41 122 | #009bbf 123 | else if _cycle >= 20 and _cycle < 30 124 | #00a4df 125 | else if _cycle >= 10 and _cycle < 20 126 | #00a4df 127 | else if _cycle >= 5 and _cycle < 10 128 | #65a095 129 | else if _cycle < -5 and _cycle >= -10 130 | #b7b763 131 | else if _cycle < -10 and _cycle >= -15 132 | #fafa6e 133 | else if _cycle < -15 and _cycle >= -20 134 | #fecb35 135 | else if _cycle < -20 and _cycle >= -25 136 | #ff9800 137 | else if _cycle < -25 and _cycle >= -31 138 | #ff7434 139 | else if _cycle < -31 and _cycle >= -37 140 | #ff5252 141 | else if _cycle < -37 and _cycle >= -41 142 | #c72e29 143 | else if _cycle < -41 144 | #910000 145 | else 146 | #92928f 147 | 148 | f_getColor1(_cycle) => 149 | if _cycle >= 73 150 | #910000 151 | else if _cycle >= 63 and _cycle < 73 152 | #c72e29 153 | else if _cycle >= 52 and _cycle < 63 154 | #ff5252 155 | else if _cycle >= 41 and _cycle < 52 156 | #ff7434 157 | else if _cycle >= 30 and _cycle < 41 158 | #ff9800 159 | else if _cycle >= 20 and _cycle < 30 160 | #fecb35 161 | else if _cycle >= 10 and _cycle < 20 162 | #fafa6e 163 | else if _cycle >= 5 and _cycle < 10 164 | #b7b763 165 | else if _cycle < -5 and _cycle >= -10 166 | #65a095 167 | else if _cycle < -10 and _cycle >= -15 168 | #00a4df 169 | else if _cycle < -15 and _cycle >= -20 170 | #00a4df 171 | else if _cycle < -20 and _cycle >= -25 172 | #009bbf 173 | else if _cycle < -25 and _cycle >= -31 174 | #009096 175 | else if _cycle < -31 and _cycle >= -37 176 | #008368 177 | else if _cycle < -37 and _cycle >= -41 178 | #007439 179 | else if _cycle < -41 180 | #006400 181 | else 182 | #92928f 183 | 184 | // -Plotting ==================================================================================== // 185 | 186 | fgColor = daily ? f_getColor(cycle_d) : f_getColor(cycle) 187 | plot(display == 'Fear & Greed Index' ? daily ? cycle_d : cycle : na, 'Psychology of The Market Cycle - Index Display', fgColor, 2) 188 | 189 | var label fgiLabel = na 190 | if not hLabel 191 | fgiLabel := label.new(time + math.round(ta.change(time) * 7), display == 'Price Candles' ? close : daily ? cycle_d : cycle, (daily ? '*Daily' : 'Daily') + ' Time Frame : ' + f_getText(cycle_d) + ' : ' + 192 | str.tostring(math.round(cycle_d, 2)) + '%\n' + (daily ? 'Current' : '*Current') + ' Time Frame (' + timeframe.period + ') : ' + f_getText(cycle) + ' : ' + 193 | str.tostring(math.round(cycle, 2)) + '%', xloc.bar_time, tooltip='Psychology of The Market Cycle by DGT\n\n' + 'Fear & Greed Index :\nDaily Time Frame : ' + f_getText(cycle_d) + ' : ' + 194 | str.tostring(math.round(cycle_d, 2)) + '%\nCurrent Time Frame (' + timeframe.period + ') : ' + f_getText(cycle) + ' : ' + 195 | str.tostring(math.round(cycle, 2)) + '%' + '%\n\nReference Sources : ' + '\n-------------------------------------------------------------------' + 196 | '\n 1 - Price Convergence/Divergence to/from its Moving Average (' + str.tostring(slowLength) + ') :\n Daily TF ' + str.tostring(math.round(pmacd_d, 2)) + '% / Current TF ' + str.tostring(math.round(pmacd, 2)) + '%' + 197 | '\n 2 - Rate of Return (Momentum/RoC), Length (' + str.tostring(slowLength) + ') :\n Daily TF ' + str.tostring(math.round(ror_d, 2)) + '% / Current TF ' + str.tostring(math.round(ror, 2)) + '%' + 198 | '\n 3 - Chaikin Money Flow, Length (' + str.tostring(fastLength) + ') :\n Daily TF ' + str.tostring(math.round(moneyFlow_d, 2)) + '% / Current TF ' + str.tostring(math.round(moneyFlow, 2)) + '% \n ps: cmf calculated only if volume data is provided' + 199 | '\n 4 - VIX - Volatility (Fear) Index, Length (' + str.tostring(slowLength) + ') :\n Daily TF ' + str.tostring(math.round(vix_d, 2)) + '% / Current TF ' + str.tostring(math.round(vix, 2)) + '%' + 200 | '\n 5 - Safe Haven Demand - Gold Demand, Length (' + str.tostring(fastLength) + ') :\n Daily TF ' + str.tostring(math.round(gold_d, 2)) + '% / Current TF ' + str.tostring(math.round(gold, 2)) + '%' + 201 | '\n\nWarren Buffett’s quote, buy when others are fearful, and sell when others are greedy', color=color.new(color.gray, 85), style=label.style_label_left, textcolor=color.gray, textalign=text.align_left) 202 | 203 | label.delete(fgiLabel[1]) 204 | 205 | var o = 0., var h = 0., var l = 0., var c = 0. 206 | 207 | if display == 'Price Candles' 208 | o := open, h := high, l := low, c := close 209 | 210 | fgColor := daily ? f_getColor1(cycle_d) : f_getColor1(cycle) 211 | 212 | if display == 'Fear & Greed Index' 213 | fgColor := na 214 | 215 | plotcandle(o, h, l, c, 'Psychology of The Market Cycle - Price Display', fgColor, fgColor, bordercolor = fgColor) 216 | 217 | var table logo = table.new(position.bottom_right, 1, 1) 218 | table.cell(logo, 0, 0, "☼☾ ", text_size = size.normal, text_color = color.teal) 219 | 220 | 221 | var table legend = table.new(position.middle_right , 1, 5) 222 | 223 | if lagendDesc 224 | if display == 'Price Candles' 225 | table.cell(legend, 0, 0, "Max Opportunity █", text_size = size.small, text_color = #006400, text_halign = text.align_right) 226 | table.cell(legend, 0, 1, "Opportunity █", text_size = size.small, text_color = #008368, text_halign = text.align_right) 227 | table.cell(legend, 0, 2, "Neutral █", text_size = size.small, text_color = #92928f, text_halign = text.align_right) 228 | table.cell(legend, 0, 3, "Risk █", text_size = size.small, text_color = #ff7434, text_halign = text.align_right) 229 | table.cell(legend, 0, 4, "Max Rsik █", text_size = size.small, text_color = #910000, text_halign = text.align_right) 230 | else 231 | table.cell(legend, 0, 0, "Extreame Greed █", text_size = size.small, text_color = #006400, text_halign = text.align_right) 232 | table.cell(legend, 0, 1, "Greed █", text_size = size.small, text_color = #008368, text_halign = text.align_right) 233 | table.cell(legend, 0, 2, "Neutral █", text_size = size.small, text_color = #92928f, text_halign = text.align_right) 234 | table.cell(legend, 0, 3, "Fear █", text_size = size.small, text_color = #ff7434, text_halign = text.align_right) 235 | table.cell(legend, 0, 4, "Extreame Fear █", text_size = size.small, text_color = #910000, text_halign = text.align_right) 236 | 237 | 238 | // ————— Filter. 239 | FilterLong = Fear 240 | FilterShort = Greed 241 | 242 | // ————— Entries. 243 | EnterLong = Fear 244 | EnterShort = Greed 245 | 246 | // ————— Stops. 247 | Atr = ta.atr(14) 248 | StopLong = math.min(ta.lowest(5), math.min(close, open) - Atr * 1.5) 249 | StopShort = math.max(ta.highest(5), math.max(close, open) + Atr * 1.5) 250 | 251 | // ————— Exits. 252 | ExitLong = Neutral 253 | ExitShort = Neutral 254 | 255 | // ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ 256 | // —————————————————————————————————————————————————————————————————————————————————————— 257 | 258 | 259 | // ————— This function ensures that no stop value equal to one of the protocol's reserved values is sent, so it isn't misinterpreted as a signal. 260 | // The fudge is in the direction where the trade's risk is decreased by a tick, so added for longs and subtracted for shorts. 261 | FudgeStop(_stop, _fudgedir) => 262 | _stop == 1.0 or _stop == 2.0 or _stop == 3.0 ? _stop + syminfo.mintick * _fudgedir : _stop 263 | 264 | // ————— Build required values corresponding to states. 265 | Filter = FilterLong ? 1 : FilterShort ? -1 : 0 266 | Entries = EnterLong ? 2 : EnterShort ? -2 : 0 267 | FudgeStop__1 = FudgeStop(StopLong, 1) 268 | FudgeStop__2 = FudgeStop(StopShort, -1) 269 | Stops = EnterLong ? FudgeStop__1 : EnterShort ? -FudgeStop__2 : 0 270 | Exits = ExitLong ? 3 : ExitShort ? -3 : 0 271 | 272 | // ————— We must decide which value will be sent in case more than one is different than 0, since only one value can be sent at each bar. 273 | // Priority is given to exits, with filter states coming last. 274 | Signal = IncludeExits and Exits != 0 ? Exits : IncludeStops and Stops != 0 ? Stops : IncludeEntries and Entries != 0 ? Entries : IncludeFilter and Filter != 0 ? Filter : na 275 | 276 | // ————— Plot signal which is to be connected to the Engine through the External Indicator field at the very bottom of the Engine's Settings/Inputs. 277 | plot(Signal, 'Signal') 278 | 279 | 280 | // ————— Plots markers. 281 | plotshape(IncludeEntries and EnterLong, 'Enter Long', style=shape.triangleup, location=location.bottom, color=color.new(color.green, 0), size=size.small, text='Enter\nLong') 282 | plotshape(IncludeEntries and EnterShort, 'Enter Short', style=shape.triangledown, location=location.top, color=color.new(color.red, 0), size=size.small, text='Enter\nShort') 283 | plotshape(IncludeExits and ExitLong, 'Exit Long', style=shape.triangledown, location=location.top, color=color.new(color.green, 0), size=size.tiny, text='Exit\nLong') 284 | plotshape(IncludeExits and ExitShort, 'Exit Short', style=shape.triangleup, location=location.bottom, color=color.new(color.red, 0), size=size.tiny, text='Exit\nShort') 285 | 286 | bgcolor(color=IncludeFilter and FilterLong ? color.new(#00FF00,93) : IncludeFilter and FilterShort ? color.new(#FF0000,90) : na, title='Filter Background') -------------------------------------------------------------------------------- /signal-ext/fg-cycle.pine: -------------------------------------------------------------------------------- 1 | //@version=5 2 | // ══════════════════════════════════════════════════════════════════════════════════════════════════ // 3 | //# * ══════════════════════════════════════════════════════════════════════════════════════════════ 4 | //# * 5 | //# * Study : Trading Psychology - Fear & Greed Index by DGT 6 | //# * Author : © dgtrd 7 | //# * 8 | //# * Revision History 9 | //# * Release : Jul 21, 2020 10 | //# * Update : Apr 09, 2021 : Added ability to display Current Time Frame's Fear and Greed Index 11 | //# * Update : Apr 09, 2022 : Added Price Candle display option with Fear & Greed weighted risk levels 12 | //# * 13 | //# * ══════════════════════════════════════════════════════════════════════════════════════════════ 14 | // ══════════════════════════════════════════════════════════════════════════════════════════════════ // 15 | 16 | indicator('Trading Psychology - Fear & Greed Index by DGT', 'F&G CYCLE ʙʏ DGT ☼☾', false, max_bars_back=252) 17 | 18 | // -Inputs ====================================================================================== // 19 | 20 | display = input.string('Fear & Greed Index', 'Display as', options=['Price Candles', 'Fear & Greed Index']) 21 | smoothLength = input.int(5, 'RMA Smoothing Length', minval=1, maxval=13) 22 | faster = input(false, 'Fear & Greed Index : Calculation with Faster Legths') 23 | daily = input(false, 'Display Daily Time Frame Calculated Value') 24 | lagendDesc = input(true, 'Show Color Legend') 25 | hLabel = input(false, 'Hide Statistical Label') 26 | 27 | 28 | fastLength = faster ? 13 : 21 29 | slowLength = faster ? 89 : 144 30 | 31 | // -Calculations ================================================================================ // 32 | 33 | pmacd = (close / ta.ema(close, slowLength) - 1) * 100 34 | pmacd_d = request.security(syminfo.tickerid, 'D', pmacd, barmerge.gaps_off, barmerge.lookahead_on) 35 | 36 | ror = (close - close[slowLength]) / close[slowLength] * 100 37 | ror_d = request.security(syminfo.tickerid, 'D', ror, barmerge.gaps_off, barmerge.lookahead_on) 38 | 39 | accDist = close == high and close == low or high == low ? 0 : (2 * close - low - high) / (high - low) 40 | nzVolume = nz(volume) 41 | moneyFlow = math.sum(accDist * nzVolume, fastLength) / math.sum(nzVolume, fastLength) * 100 42 | 43 | accDist_d = request.security(syminfo.tickerid, 'D', accDist, barmerge.gaps_off, barmerge.lookahead_on) 44 | nzVolume_d = request.security(syminfo.tickerid, 'D', nzVolume) 45 | moneyFlow_d = math.sum(accDist_d * nzVolume_d, fastLength) / math.sum(nzVolume_d, fastLength) * 100 46 | 47 | vix = request.security('VIX', timeframe.period, -(close / ta.ema(close, slowLength) - 1) * 100, barmerge.gaps_off, barmerge.lookahead_on) 48 | vix_d = request.security('VIX', 'D', -(close / ta.ema(close, slowLength) - 1) * 100, barmerge.gaps_off, barmerge.lookahead_on) 49 | 50 | gold = request.security('GOLD', timeframe.period, -(1 - close[fastLength] / close) * 100, barmerge.gaps_off, barmerge.lookahead_on) 51 | gold_d = request.security('GOLD', 'D', -(1 - close[fastLength] / close) * 100, barmerge.gaps_off, barmerge.lookahead_on) 52 | 53 | cycle_raw = nzVolume ? math.avg(pmacd, ror, moneyFlow, vix, gold) : math.avg(pmacd, ror, vix, gold) 54 | cycle_raw_d = nzVolume_d ? math.avg(pmacd_d, ror_d, moneyFlow_d, vix_d, gold_d) : math.avg(pmacd_d, ror_d, vix_d, gold_d) 55 | 56 | cycle = ta.rma(cycle_raw, smoothLength) 57 | cycle_d = ta.rma(cycle_raw_d, smoothLength) 58 | 59 | f_getText(_cycle) => 60 | if _cycle > 73 61 | 'Extreame Greed' 62 | else if _cycle > 33 63 | 'Greed' 64 | else if _cycle < -25 65 | 'Fear' 66 | else if _cycle < -41 67 | 'Extreame Fear' 68 | else 69 | 'Neutral' 70 | 71 | f_getColor(_cycle) => 72 | if _cycle >= 73 73 | #006400 74 | else if _cycle >= 63 and _cycle < 73 75 | #007439 76 | else if _cycle >= 52 and _cycle < 63 77 | #008368 78 | else if _cycle >= 41 and _cycle < 52 79 | #009096 80 | else if _cycle >= 30 and _cycle < 41 81 | #009bbf 82 | else if _cycle >= 20 and _cycle < 30 83 | #00a4df 84 | else if _cycle >= 10 and _cycle < 20 85 | #00a4df 86 | else if _cycle >= 5 and _cycle < 10 87 | #65a095 88 | else if _cycle < -5 and _cycle >= -10 89 | #b7b763 90 | else if _cycle < -10 and _cycle >= -15 91 | #fafa6e 92 | else if _cycle < -15 and _cycle >= -20 93 | #fecb35 94 | else if _cycle < -20 and _cycle >= -25 95 | #ff9800 96 | else if _cycle < -25 and _cycle >= -31 97 | #ff7434 98 | else if _cycle < -31 and _cycle >= -37 99 | #ff5252 100 | else if _cycle < -37 and _cycle >= -41 101 | #c72e29 102 | else if _cycle < -41 103 | #910000 104 | else 105 | #92928f 106 | 107 | f_getColor1(_cycle) => 108 | if _cycle >= 73 109 | #910000 110 | else if _cycle >= 63 and _cycle < 73 111 | #c72e29 112 | else if _cycle >= 52 and _cycle < 63 113 | #ff5252 114 | else if _cycle >= 41 and _cycle < 52 115 | #ff7434 116 | else if _cycle >= 30 and _cycle < 41 117 | #ff9800 118 | else if _cycle >= 20 and _cycle < 30 119 | #fecb35 120 | else if _cycle >= 10 and _cycle < 20 121 | #fafa6e 122 | else if _cycle >= 5 and _cycle < 10 123 | #b7b763 124 | else if _cycle < -5 and _cycle >= -10 125 | #65a095 126 | else if _cycle < -10 and _cycle >= -15 127 | #00a4df 128 | else if _cycle < -15 and _cycle >= -20 129 | #00a4df 130 | else if _cycle < -20 and _cycle >= -25 131 | #009bbf 132 | else if _cycle < -25 and _cycle >= -31 133 | #009096 134 | else if _cycle < -31 and _cycle >= -37 135 | #008368 136 | else if _cycle < -37 and _cycle >= -41 137 | #007439 138 | else if _cycle < -41 139 | #006400 140 | else 141 | #92928f 142 | 143 | // -Plotting ==================================================================================== // 144 | 145 | fgColor = daily ? f_getColor(cycle_d) : f_getColor(cycle) 146 | plot(display == 'Fear & Greed Index' ? daily ? cycle_d : cycle : na, 'Psychology of The Market Cycle - Index Display', fgColor, 2) 147 | 148 | var label fgiLabel = na 149 | if not hLabel 150 | fgiLabel := label.new(time + math.round(ta.change(time) * 7), display == 'Price Candles' ? close : daily ? cycle_d : cycle, (daily ? '*Daily' : 'Daily') + ' Time Frame : ' + f_getText(cycle_d) + ' : ' + 151 | str.tostring(math.round(cycle_d, 2)) + '%\n' + (daily ? 'Current' : '*Current') + ' Time Frame (' + timeframe.period + ') : ' + f_getText(cycle) + ' : ' + 152 | str.tostring(math.round(cycle, 2)) + '%', xloc.bar_time, tooltip='Psychology of The Market Cycle by DGT\n\n' + 'Fear & Greed Index :\nDaily Time Frame : ' + f_getText(cycle_d) + ' : ' + 153 | str.tostring(math.round(cycle_d, 2)) + '%\nCurrent Time Frame (' + timeframe.period + ') : ' + f_getText(cycle) + ' : ' + 154 | str.tostring(math.round(cycle, 2)) + '%' + '%\n\nReference Sources : ' + '\n-------------------------------------------------------------------' + 155 | '\n 1 - Price Convergence/Divergence to/from its Moving Average (' + str.tostring(slowLength) + ') :\n Daily TF ' + str.tostring(math.round(pmacd_d, 2)) + '% / Current TF ' + str.tostring(math.round(pmacd, 2)) + '%' + 156 | '\n 2 - Rate of Return (Momentum/RoC), Length (' + str.tostring(slowLength) + ') :\n Daily TF ' + str.tostring(math.round(ror_d, 2)) + '% / Current TF ' + str.tostring(math.round(ror, 2)) + '%' + 157 | '\n 3 - Chaikin Money Flow, Length (' + str.tostring(fastLength) + ') :\n Daily TF ' + str.tostring(math.round(moneyFlow_d, 2)) + '% / Current TF ' + str.tostring(math.round(moneyFlow, 2)) + '% \n ps: cmf calculated only if volume data is provided' + 158 | '\n 4 - VIX - Volatility (Fear) Index, Length (' + str.tostring(slowLength) + ') :\n Daily TF ' + str.tostring(math.round(vix_d, 2)) + '% / Current TF ' + str.tostring(math.round(vix, 2)) + '%' + 159 | '\n 5 - Safe Haven Demand - Gold Demand, Length (' + str.tostring(fastLength) + ') :\n Daily TF ' + str.tostring(math.round(gold_d, 2)) + '% / Current TF ' + str.tostring(math.round(gold, 2)) + '%' + 160 | '\n\nWarren Buffett’s quote, buy when others are fearful, and sell when others are greedy', color=color.new(color.gray, 85), style=label.style_label_left, textcolor=color.gray, textalign=text.align_left) 161 | 162 | label.delete(fgiLabel[1]) 163 | 164 | var o = 0., var h = 0., var l = 0., var c = 0. 165 | 166 | if display == 'Price Candles' 167 | o := open, h := high, l := low, c := close 168 | 169 | fgColor := daily ? f_getColor1(cycle_d) : f_getColor1(cycle) 170 | 171 | if display == 'Fear & Greed Index' 172 | fgColor := na 173 | 174 | plotcandle(o, h, l, c, 'Psychology of The Market Cycle - Price Display', fgColor, fgColor, bordercolor = fgColor) 175 | 176 | var table logo = table.new(position.bottom_right, 1, 1) 177 | table.cell(logo, 0, 0, "☼☾ ", text_size = size.normal, text_color = color.teal) 178 | 179 | 180 | var table legend = table.new(position.middle_right , 1, 5) 181 | 182 | if lagendDesc 183 | if display == 'Price Candles' 184 | table.cell(legend, 0, 0, "Max Opportunity █", text_size = size.small, text_color = #006400, text_halign = text.align_right) 185 | table.cell(legend, 0, 1, "Opportunity █", text_size = size.small, text_color = #008368, text_halign = text.align_right) 186 | table.cell(legend, 0, 2, "Neutral █", text_size = size.small, text_color = #92928f, text_halign = text.align_right) 187 | table.cell(legend, 0, 3, "Risk █", text_size = size.small, text_color = #ff7434, text_halign = text.align_right) 188 | table.cell(legend, 0, 4, "Max Rsik █", text_size = size.small, text_color = #910000, text_halign = text.align_right) 189 | else 190 | table.cell(legend, 0, 0, "Extreame Greed █", text_size = size.small, text_color = #006400, text_halign = text.align_right) 191 | table.cell(legend, 0, 1, "Greed █", text_size = size.small, text_color = #008368, text_halign = text.align_right) 192 | table.cell(legend, 0, 2, "Neutral █", text_size = size.small, text_color = #92928f, text_halign = text.align_right) 193 | table.cell(legend, 0, 3, "Fear █", text_size = size.small, text_color = #ff7434, text_halign = text.align_right) 194 | table.cell(legend, 0, 4, "Extreame Fear █", text_size = size.small, text_color = #910000, text_halign = text.align_right) -------------------------------------------------------------------------------- /signal-ext/signal-bte.pine: -------------------------------------------------------------------------------- 1 | //@version=5 2 | //@author=LucF, for PineCoders 3 | 4 | // Signal for Backtesting-Trading Engine [PineCoders] 5 | // v1.3, 2019.06.21 02:58 6 | 7 | // PineCoders, Tools and ideas for all Pine coders. 8 | // This script is referenced from the PineCoders FAQ & Code here: http://www.pinecoders.com/faq_and_code/#how-can-i-use-one-scripts-output-as-an-input-into-another 9 | // Documentation: https://www.tradingview.com/script/y4CvTwRo-Signal-for-Backtesting-Trading-Engine-PineCoders/ 10 | 11 | // This script is an example of how to configure a signal for the PineCoders Backtesting-Trading Engine 12 | // which you will find here: https://www.tradingview.com/script/dYqL95JB-Backtesting-Trading-Engine-PineCoders/ 13 | // We used the built-in MACD indicator as a template. The script's signal adheres to the following protocol: 14 | 15 | // EXTERNAL SIGNAL PROTOCOL 16 | // Only one external indicator can be connected to a script; in order to leverage its use to the fullest, 17 | // the engine provides options to use it as either and entry signal, an entry/exit signal or a filter. 18 | // When used as an entry signal, you can also use the signal to provide the entry stop. Here’s how this works. 19 | // - For filter state: supply +1.0 for bull (long entries allowed), -1.0 for bear (short entries allowed). 20 | // - For entry signals: supply +2.0 for long, -2.0 for short. 21 | // - For exit signals: supply +3.0 for exit from long, -3.0 for exit from short. 22 | // - To send an entry stop level with entry signal: Send positive stop level for long entry 23 | // (e.g. 103.33 to enter a long with a stop at 103.33), negative stop level for short entry 24 | // (e.g. -103.33 to enter a short with a stop at 103.33). If you use this feature, 25 | // your indicator will have to check for exact stop levels of 1.0, 2.0 or 3.0 and their negative counterparts, 26 | // and fudge them with a tick in order to avoid confusion with other signals in the protocol. 27 | // Note that each use must be confirmed in the corresponding section of the script's Settings/Inputs. 28 | 29 | // CONFIGURATION OF ALERTS 30 | // Entry and Event alerts will generally be configured to trigger Once Per Bar Close. 31 | // Exit alerts can be configured to trigger Once Per Bar Close or Once Per Bar, depending on how fast 32 | // you want them. Once Per Bar means the alert will trigger the moment the condition is first encountered 33 | // during the realtime bar, instead of at the bar's close. 34 | // Entry and Exit alerts trigger at the same bar where the relevant marker will appear on the chart. 35 | // Event alerts trigger on the bar following the appearance of their respective marker on the chart. 36 | 37 | indicator('Signal for Backtesting-Trading Engine [PineCoders]') 38 | 39 | // Allow selection of signal content. 40 | IncludeFilter = input(true, 'Include Filter') 41 | IncludeEntries = input(true, 'Include Entries') 42 | IncludeStops = input(true, 'Include Stops with Entries') 43 | IncludeExits = input(true, 'Include Exits') 44 | 45 | 46 | // —————————————————————————————————————————————————————————————————————————————————————— 47 | // ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ 48 | // This part is the section where you would put your own indicator and define conditions. 49 | 50 | // ————— TV built-in MACD code. 51 | fastLength = input(12) 52 | slowlength = input(26) 53 | MACDLength = input(9) 54 | MACD = ta.ema(close, fastLength) - ta.ema(close, slowlength) 55 | aMACD = ta.ema(MACD, MACDLength) 56 | 57 | // ————— Filter. 58 | FilterLong = MACD > 0 59 | FilterShort = MACD < 0 60 | 61 | // ————— Entries. 62 | EnterLong = ta.crossover(MACD, 0) 63 | EnterShort = ta.crossunder(MACD, 0) 64 | 65 | // ————— Stops. 66 | Atr = ta.atr(14) 67 | StopLong = math.min(ta.lowest(5), math.min(close, open) - Atr * 1.5) 68 | StopShort = math.max(ta.highest(5), math.max(close, open) + Atr * 1.5) 69 | 70 | // ————— Exits. 71 | ExitLong = ta.crossunder(MACD, aMACD) and MACD > 0 72 | ExitShort = ta.crossover(MACD, aMACD) and MACD < 0 73 | 74 | // ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ 75 | // —————————————————————————————————————————————————————————————————————————————————————— 76 | 77 | 78 | // ————— This function ensures that no stop value equal to one of the protocol's reserved values is sent, so it isn't misinterpreted as a signal. 79 | // The fudge is in the direction where the trade's risk is decreased by a tick, so added for longs and subtracted for shorts. 80 | FudgeStop(_stop, _fudgedir) => 81 | _stop == 1.0 or _stop == 2.0 or _stop == 3.0 ? _stop + syminfo.mintick * _fudgedir : _stop 82 | 83 | // ————— Build required values corresponding to states. 84 | Filter = FilterLong ? 1 : FilterShort ? -1 : 0 85 | Entries = EnterLong ? 2 : EnterShort ? -2 : 0 86 | FudgeStop__1 = FudgeStop(StopLong, 1) 87 | FudgeStop__2 = FudgeStop(StopShort, -1) 88 | Stops = EnterLong ? FudgeStop__1 : EnterShort ? -FudgeStop__2 : 0 89 | Exits = ExitLong ? 3 : ExitShort ? -3 : 0 90 | 91 | // ————— We must decide which value will be sent in case more than one is different than 0, since only one value can be sent at each bar. 92 | // Priority is given to exits, with filter states coming last. 93 | Signal = IncludeExits and Exits != 0 ? Exits : IncludeStops and Stops != 0 ? Stops : IncludeEntries and Entries != 0 ? Entries : IncludeFilter and Filter != 0 ? Filter : na 94 | 95 | // ————— Plot signal which is to be connected to the Engine through the External Indicator field at the very bottom of the Engine's Settings/Inputs. 96 | plot(Signal, 'Signal') 97 | 98 | 99 | // ————— Plots markers. 100 | plotshape(IncludeEntries and EnterLong, 'Enter Long', style=shape.triangleup, location=location.bottom, color=color.new(color.green, 0), size=size.small, text='Enter\nLong') 101 | plotshape(IncludeEntries and EnterShort, 'Enter Short', style=shape.triangledown, location=location.top, color=color.new(color.red, 0), size=size.small, text='Enter\nShort') 102 | plotshape(IncludeExits and ExitLong, 'Exit Long', style=shape.triangledown, location=location.top, color=color.new(color.green, 0), size=size.tiny, text='Exit\nLong') 103 | plotshape(IncludeExits and ExitShort, 'Exit Short', style=shape.triangleup, location=location.bottom, color=color.new(color.red, 0), size=size.tiny, text='Exit\nShort') 104 | 105 | bgcolor(color=IncludeFilter and FilterLong ? color.new(#00FF00,93) : IncludeFilter and FilterShort ? color.new(#FF0000,90) : na, title='Filter Background') 106 | 107 | -------------------------------------------------------------------------------- /signal-ext/signal.pine: -------------------------------------------------------------------------------- 1 | //@version=3 2 | //@author=LucF, for PineCoders 3 | 4 | // Signal for Backtesting-Trading Engine [PineCoders] 5 | // v1.3, 2019.06.21 02:58 6 | 7 | // PineCoders, Tools and ideas for all Pine coders. 8 | // This script is referenced from the PineCoders FAQ & Code here: http://www.pinecoders.com/faq_and_code/#how-can-i-use-one-scripts-output-as-an-input-into-another 9 | // Documentation: https://www.tradingview.com/script/y4CvTwRo-Signal-for-Backtesting-Trading-Engine-PineCoders/ 10 | 11 | // This script is an example of how to configure a signal for the PineCoders Backtesting-Trading Engine 12 | // which you will find here: https://www.tradingview.com/script/dYqL95JB-Backtesting-Trading-Engine-PineCoders/ 13 | // We used the built-in MACD indicator as a template. The script's signal adheres to the following protocol: 14 | 15 | // EXTERNAL SIGNAL PROTOCOL 16 | // Only one external indicator can be connected to a script; in order to leverage its use to the fullest, 17 | // the engine provides options to use it as either and entry signal, an entry/exit signal or a filter. 18 | // When used as an entry signal, you can also use the signal to provide the entry stop. Here’s how this works. 19 | // - For filter state: supply +1.0 for bull (long entries allowed), -1.0 for bear (short entries allowed). 20 | // - For entry signals: supply +2.0 for long, -2.0 for short. 21 | // - For exit signals: supply +3.0 for exit from long, -3.0 for exit from short. 22 | // - To send an entry stop level with entry signal: Send positive stop level for long entry 23 | // (e.g. 103.33 to enter a long with a stop at 103.33), negative stop level for short entry 24 | // (e.g. -103.33 to enter a short with a stop at 103.33). If you use this feature, 25 | // your indicator will have to check for exact stop levels of 1.0, 2.0 or 3.0 and their negative counterparts, 26 | // and fudge them with a tick in order to avoid confusion with other signals in the protocol. 27 | // Note that each use must be confirmed in the corresponding section of the script's Settings/Inputs. 28 | 29 | // CONFIGURATION OF ALERTS 30 | // Entry and Event alerts will generally be configured to trigger Once Per Bar Close. 31 | // Exit alerts can be configured to trigger Once Per Bar Close or Once Per Bar, depending on how fast 32 | // you want them. Once Per Bar means the alert will trigger the moment the condition is first encountered 33 | // during the realtime bar, instead of at the bar's close. 34 | // Entry and Exit alerts trigger at the same bar where the relevant marker will appear on the chart. 35 | // Event alerts trigger on the bar following the appearance of their respective marker on the chart. 36 | 37 | study("Signal for Backtesting-Trading Engine [PineCoders]") 38 | 39 | // Allow selection of signal content. 40 | IncludeFilter = input(true, "Include Filter") 41 | IncludeEntries = input(true, "Include Entries") 42 | IncludeStops = input(true, "Include Stops with Entries") 43 | IncludeExits = input(true, "Include Exits") 44 | 45 | 46 | // —————————————————————————————————————————————————————————————————————————————————————— 47 | // ▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼▼ 48 | // This part is the section where you would put your own indicator and define conditions. 49 | 50 | // ————— Chop & explode code. 51 | src = close, len = input(14, minval=1, title="Length") 52 | up = rma(max(change(src), 0), len) 53 | down = rma(-min(change(src), 0), len) 54 | rsi = down == 0 ? 100 : up == 0 ? 0 : 100 - (100 / (1 + up / down)) 55 | 56 | plot(rsi, color=black) 57 | 58 | change1 = rsi > 60 ? blue :na 59 | change2 = rsi < 40 ? purple: na 60 | 61 | // ————— Filter. 62 | FilterLong = change1 63 | FilterShort = change2 64 | 65 | // ————— Entries. 66 | EnterLong = up 67 | EnterShort = down 68 | 69 | 70 | // ▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲▲ 71 | // —————————————————————————————————————————————————————————————————————————————————————— 72 | 73 | p1 = plot(rsi, style=linebr, linewidth=3, color=change1) 74 | p2 = plot(rsi, style=linebr, linewidth=3, color=change2) 75 | 76 | 77 | band1 = hline(70) 78 | band2 = hline(30) 79 | band3 = hline(60) 80 | band4 = hline(40) 81 | midline= hline(50) 82 | band5 = hline(55) 83 | band6 = hline(45) 84 | fill(band1, band3, color=green, transp=90) 85 | fill(band3, band4, color=yellow, transp=90) 86 | fill(band4, band2, color=red, transp=90) 87 | fill(band5,band6, color=black, transp=50) 88 | 89 | cond1 = rsi > 60 and close[1] < rsi and close > 0 ? 1 : 0 90 | barcolor(cond1 ? blue : na) 91 | 92 | cond2 = rsi < 40 and close[1] < rsi and close > 0 ? 1 : 0 93 | barcolor(cond2 ? purple : na) 94 | 95 | cond3 = rsi > 55 and close[1] < rsi and close < 60 ? 1 : 0 96 | barcolor(cond3 ? yellow : na) 97 | 98 | cond4 = rsi > 45 and close[1] < rsi and close [1] < 55 ? 1 : 0 99 | barcolor(cond4 ? black : na) 100 | 101 | cond5 = rsi > 40 and close[1] < rsi and close [1] < 55 ? 1 : 0 102 | barcolor(cond5 ? yellow : na) 103 | 104 | 105 | 106 | // ————— Build required values corresponding to states. 107 | Filter = FilterLong ? 1 : FilterShort ? -1 : 0 108 | Entries = EnterLong ? 2 : EnterShort ? -2 : 0 109 | 110 | // ————— We must decide which value will be sent in case more than one is different than 0, since only one value can be sent at each bar. 111 | // Priority is given to exits, with filter states coming last. 112 | Signal = IncludeExits and Exits != 0 ? Exits : IncludeEntries and Entries != 0 ? Entries : IncludeFilter and Filter != 0 ? Filter : na 113 | 114 | // ————— Plot signal which is to be connected to the Engine through the External Indicator field at the very bottom of the Engine's Settings/Inputs. 115 | plot( Signal, "Signal") 116 | 117 | 118 | // ————— Plots markers. 119 | plotshape(IncludeEntries and EnterLong, "Enter Long", style=shape.triangleup, location=location.bottom, color=green, size=size.small, text="Enter\nLong") 120 | plotshape(IncludeEntries and EnterShort, "Enter Short", style=shape.triangledown, location=location.top, color=red, size=size.small, text="Enter\nShort") 121 | 122 | bgcolor(color = IncludeFilter and FilterLong ? lime : IncludeFilter and FilterShort ? red : na, title = "Filter Background") 123 | -------------------------------------------------------------------------------- /study-dynamic-variable-alert.pine: -------------------------------------------------------------------------------- 1 | [In reply to Anton_fmrly_fomobro] 2 | [ Photo ] 3 | Example using dynamic variable alerts message from within an indicator() alertcondition: 4 | 5 | //@version=5 6 | indicator(title = "Study Alert Plot Example") 7 | plot(close, title = 'priceClose') 8 | alertcondition(close, title ="~Alert Close", message = 'Closing Price of {{ticker}} is: ${{plot("priceClose")}}') 9 | 10 | — 11 | NOTICE: For strategy() type scripts, use placeholder below in the alert creation Message field: 12 | 13 | {{strategy.order.alert_message}} 14 | 15 | ^ This placeholder will then call the messages from your strategy.x() statements: 16 | 17 | strategy.x(alert_message="blah {{placeholder_A}}") 18 | 19 | — 20 | Alerts Dynamic Variable 21 | PineCoders FAQ & Code: 22 | • How do I make an alert available from my script? 23 | 24 | (https://www.pinecoders.com/faq_and_code/#alerts)TradingView Help: 25 | • How to use a variable value in Alert? 26 | (https://www.tradingview.com/support/solutions/43000531021-how-to-use-a-variable-value-in-alert/) • Strategy Alerts 27 | (https://www.tradingview.com/support/solutions/43000481368/)TradingView Blog: 28 | • Our New Alerts Allow for Dynamic Messages 29 | (https://www.tradingview.com/blog/en/our-new-alerts-allow-for-dynamic-messages-22588/) • Strategy Alerts Are Live Now 30 | (https://www.tradingview.com/blog/en/strategy-alerts-are-live-now-18770/) • Introducing Variables in Alerts 31 | (https://www.tradingview.com/blog/en/introducing-variables-in-alerts-14880/)User Manual: Alerts 32 | (https://www.tradingview.com/pine-script-docs/en/v5/concepts/Alerts.html)Language Reference: alert 33 | 34 | (https://www.tradingview.com/pine-script-reference/v5/#fun_alert)Kodify: 35 | • How To Use Variables with TradingView alerts 36 | 37 | (https://kodify.net/tradingview/alerts/alert-variables/)#howto #alert #alerts 38 | --------------------------------------------------------------------------------