├── README.md └── main /README.md: -------------------------------------------------------------------------------- 1 | # Grid Spot Algorithm Trading 2 | 3 | Grid trading model for algorithmic trading. 4 | 5 | The released code is written in Pine Script language for Trading View (Version 5). The script is set up as a strategy, giving you the ability to backtest all markets. You can easily turn it into an indicator by removing the market entry and exit functions. 6 | 7 | This is a long only strategy for spot assets. 8 | 9 | # HOW IT WORKS 10 | Grid trading is a trading strategy where an investor creates a so-called "price grid". The basic idea of the strategy is to repeatedly buy at the pre-specified price and then wait for the price to rise above that level and then sell the position (and vice versa with shorting or hedging). 11 | 12 | ![Schermata 2022-09-05 alle 20 12 16](https://user-images.githubusercontent.com/100917872/188499241-48b30ff8-4b87-42f7-a4cc-aee1bbe30ebd.png) 13 | 14 | # FEATURES 15 | 1) Grids: This algorithm has a total of 10 grids. 16 | 2) Take profit: The trader can increase or decrease the distance between the grids from the User Interface panel, the distance between one grid and another represents the take profit. 17 | 3) Management: The algorithm buys 10% of the capital every time the price breaks down a grid and sells during a rise to the next higher grid. The initial capital is invested in 10 sizes which represent 10% of the capital per trade. 18 | 4) Stop Loss: The algorithm knows no stop loss as long as it is not activated from the User Interface panel. By activating the stop loss from the User Interface panel the algorithm will insert a close condition on all trades which will be calculated from the last lower grid. 19 | 6) Trades: Trades are opened only if the price is within the grid. If the market leaves the grid the algorithm will not buy new positions or sell new positions. 20 | 7) Optimal market conditions: The favorable market for this algorithm is the sideways market. 21 | 22 | # MODEL'S LIMITATION 23 | The trader must take into account that this is a static model. It only works perfectly well if the market is in a sideways trend and incurs heavy losses if the market takes a bearish trend. The model is unusable for an bull trend. The trader must therefore carefully analyze the market where he intends to use this strategy, making sure that the price is in a sideways trend. 24 | 25 | # USES 26 | Indispensable research and backtesting tool for those using bots for their investments. The algorithm produces a backtesting of the strategy for past history. It is used by professional traders to understand if this strategy has been profitable on a market and what parameters to use for bots using this strategy (Kucoin, Binance etc.). 27 | 28 | 29 | # SET UP 30 | 1) Copy the code 31 | 2) Paste the code on your Pine editor console 32 | 3) Save 33 | 4) Run 34 | 35 | # New Releases 5-11-2022 36 | 37 | 1. Performance bug fix: the strategy had several output issues. The Trading View performance generator was offering an inappropriate calculation for this type of model. Therefore, we integrated a table showing the performance of the strategy correctly. 38 | 39 | 2. Improved User Interface: we boost the code and created a better user experience. Descriptions and confirmation tabs have been added. Many users experienced difficulty in setting up on the algorithm chart. This update has eliminated this issue definitely. 40 | 41 | 3. Grid improvement: the design of the grid has been improved, creating a refined design tool. The old grid has been replaced with a new one that has a lighter and more intuitive design. 42 | 43 | 4. Update of functions that will be deprecated: functions that will be deprecated in future updates of Pine Script have been removed. This will allow everyone to have a super tool that will run with any problem during next Pine Script updates. 44 | -------------------------------------------------------------------------------- /main: -------------------------------------------------------------------------------- 1 | //@version=5 2 | strategy(title = 'Grid Spot Trading Algorithm', 3 | overlay = true, 4 | calc_on_every_tick = true, 5 | initial_capital = 1000, 6 | commission_type = strategy.commission.percent, 7 | commission_value = 0.03, 8 | pyramiding = 100, 9 | default_qty_type = strategy.percent_of_equity, 10 | process_orders_on_close = true, 11 | close_entries_rule = 'ANY') 12 | 13 | //--------------------------------------------------------------- 14 | 15 | startDate = input.int(title="START DAY DATE", 16 | defval=1, minval=1, maxval=31, 17 | group = '1. DATE RANGE: Select the date range for backtesting', 18 | tooltip = "Enter the day of start backtesting.") 19 | startMonth = input.int(title="START MONTH", 20 | defval=1, minval=1, maxval=12, 21 | group = '1. DATE RANGE: Select the date range for backtesting', 22 | tooltip = "Enter the month of start backtesting.") 23 | startYear = input.int(title="START YEAR", 24 | defval=2018, minval=1800, maxval=2100, 25 | group = '1. DATE RANGE: Select the date range for backtesting', 26 | tooltip = "Enter the year of start backtesting.") 27 | 28 | endDate = input.int(title="END DAY DATE", 29 | defval=30, minval=1, maxval=31, 30 | group = '1. DATE RANGE: Select the date range for backtesting', 31 | tooltip = "Enter the day of end backtesting.") 32 | endMonth = input.int(title="END MONTH", 33 | defval=12, minval=1, maxval=12, 34 | group = '1. DATE RANGE: Select the date range for backtesting', 35 | tooltip = "Enter the month of end backtesting.") 36 | endYear = input.int(title="END YEAR", 37 | defval=2022, minval=1800, maxval=2100, 38 | group = '1. DATE RANGE: Select the date range for backtesting', 39 | tooltip = "Enter the year of end backtesting.") 40 | 41 | inDateRange = (time >= timestamp(syminfo.timezone, startYear, 42 | startMonth, startDate, 0, 0)) and 43 | (time < timestamp(syminfo.timezone, endYear, endMonth, endDate, 0, 0)) 44 | 45 | //--------------------------------------------------------------- 46 | 47 | PercentProfit = input.float(defval = 0.50, 48 | title = "GRID PERCENT VALUE", 49 | group = "2. GRID CUSTOM SETTING", 50 | tooltip = "This feature refers to the percentage value of each grid. The value you enter here will allow you to create your custom grid.", 51 | confirm = true) 52 | 53 | UpperLimit = input.price(defval = 0.00, 54 | title = 'TOP GRID POSITION', 55 | group = "2. GRID CUSTOM SETTING", 56 | confirm = true, 57 | tooltip = "This feature allows you to set the initial price level of your grid. Click on a price level to start.") 58 | 59 | feePerTrade = input.float(defval = 0.03, minval = 0.00, maxval = 100, 60 | title = "FEE PER TRADE", 61 | group = "2. GRID CUSTOM SETTING", 62 | tooltip = "Set the commission cost your broker charges you for each trade.", 63 | confirm = true) 64 | 65 | //--------------------------------------------------------------- 66 | 67 | x1 = (100 - (PercentProfit * 1)) / 100 68 | x2 = (100 - (PercentProfit * 2)) / 100 69 | x3 = (100 - (PercentProfit * 3)) / 100 70 | x4 = (100 - (PercentProfit * 4)) / 100 71 | x5 = (100 - (PercentProfit * 5)) / 100 72 | x6 = (100 - (PercentProfit * 6)) / 100 73 | x7 = (100 - (PercentProfit * 7)) / 100 74 | x8 = (100 - (PercentProfit * 8)) / 100 75 | x9 = (100 - (PercentProfit * 9)) / 100 76 | x10 = (100 - (PercentProfit * 10)) / 100 77 | 78 | LowerLimit = UpperLimit * x10 79 | 80 | //--------------------------------------------------------------- 81 | 82 | Linea_A = float(UpperLimit * x1) 83 | Linea_B = float(UpperLimit * x2) 84 | Linea_C = float(UpperLimit * x3) 85 | Linea_D = float(UpperLimit * x4) 86 | Linea_50Percent = float(UpperLimit * x5) 87 | Linea_E = float(UpperLimit * x6) 88 | Linea_F = float(UpperLimit * x7) 89 | Linea_G = float(UpperLimit * x8) 90 | Linea_H = float(UpperLimit * x9) 91 | 92 | //--------------------------------------------------------------- 93 | 94 | Long1 = ta.crossunder(close, Linea_A) and inDateRange 95 | Long2 = ta.crossunder(close, Linea_B) and inDateRange 96 | Long3 = ta.crossunder(close, Linea_C) and inDateRange 97 | Long4 = ta.crossunder(close, Linea_D) and inDateRange 98 | Long5 = ta.crossunder(close, Linea_50Percent) and inDateRange 99 | Long6 = ta.crossunder(close, Linea_E) and inDateRange 100 | Long7 = ta.crossunder(close, Linea_F) and inDateRange 101 | Long8 = ta.crossunder(close, Linea_G) and inDateRange 102 | Long9 = ta.crossunder(close, Linea_H) and inDateRange 103 | Long10 = ta.crossunder(close, LowerLimit) and inDateRange 104 | 105 | //--------------------------------------------------------------- 106 | 107 | isExit1 = ta.crossover(close, UpperLimit) 108 | isExit2 = ta.crossover(close, Linea_A) 109 | isExit3 = ta.crossover(close, Linea_B) 110 | isExit4 = ta.crossover(close, Linea_C) 111 | isExit5 = ta.crossover(close, Linea_D) 112 | isExit6 = ta.crossover(close, Linea_50Percent) 113 | isExit7 = ta.crossover(close, Linea_E) 114 | isExit8 = ta.crossover(close, Linea_F) 115 | isExit9 = ta.crossover(close, Linea_G) 116 | isExit10 = ta.crossover(close, Linea_H) 117 | 118 | //--------------------------------------------------------------- 119 | 120 | plot(LowerLimit, color = color.purple, linewidth = 2) 121 | plot(UpperLimit, color = color.purple, linewidth = 2) 122 | 123 | plot(Linea_A, color = color.blue, linewidth = 1) 124 | plot(Linea_B, color = color.blue, linewidth = 1) 125 | plot(Linea_C, color = color.blue, linewidth = 1) 126 | plot(Linea_D, color = color.blue, linewidth = 1) 127 | plot(Linea_50Percent, color = color.blue, linewidth = 1) 128 | plot(Linea_E, color = color.blue, linewidth = 1) 129 | plot(Linea_F, color = color.blue, linewidth = 1) 130 | plot(Linea_G, color = color.blue, linewidth = 1) 131 | plot(Linea_H, color = color.blue, linewidth = 1) 132 | 133 | //--------------------------------------------------------------- 134 | 135 | fill(plot(UpperLimit), 136 | plot(Linea_A), 137 | color = color.new(color.purple, 90)) 138 | fill(plot(Linea_A), 139 | plot(Linea_B), 140 | color = color.new(color.purple, 85)) 141 | fill(plot(Linea_B), 142 | plot(Linea_C), 143 | color = color.new(color.purple, 80)) 144 | fill(plot(Linea_C), 145 | plot(Linea_D), 146 | color = color.new(color.purple, 70)) 147 | fill(plot(Linea_D), 148 | plot(Linea_50Percent), 149 | color = color.new(color.purple, 60)) 150 | fill(plot(Linea_50Percent), 151 | plot(Linea_E), 152 | color = color.new(color.purple, 60)) 153 | fill(plot(Linea_E), 154 | plot(Linea_F), 155 | color = color.new(color.purple, 70)) 156 | fill(plot(Linea_F), 157 | plot(Linea_G), 158 | color = color.new(color.purple, 80)) 159 | fill(plot(Linea_G), 160 | plot(Linea_H), 161 | color = color.new(color.purple, 85)) 162 | fill(plot(Linea_H), 163 | plot(LowerLimit), 164 | color = color.new(color.purple, 90)) 165 | 166 | //--------------------------------------------------------------- 167 | 168 | if Long1 169 | strategy.entry(id = "LONG1", 170 | direction = strategy.long, 171 | qty = 1) 172 | if isExit1 173 | strategy.exit(id = "EXIT1", 174 | from_entry = "LONG1", 175 | qty = 100, 176 | stop = UpperLimit, 177 | profit = UpperLimit) 178 | 179 | if Long2 180 | strategy.entry(id = "LONG2", 181 | direction = strategy.long, 182 | qty = 1) 183 | if isExit2 184 | strategy.exit(id = "EXIT2", 185 | from_entry = "LONG2", 186 | qty = 100, 187 | stop = Linea_A, 188 | limit = Linea_A) 189 | 190 | if Long3 191 | strategy.entry(id = "LONG3", 192 | direction = strategy.long, 193 | qty = 1) 194 | if isExit3 195 | strategy.exit(id = "EXIT3", 196 | from_entry = "LONG3", 197 | qty = 100, 198 | stop = Linea_B, 199 | limit = Linea_B) 200 | 201 | if Long4 202 | strategy.entry(id = "LONG4", 203 | direction = strategy.long, 204 | qty = 1) 205 | if isExit4 206 | strategy.exit(id = "EXIT4", 207 | from_entry = "LONG4", 208 | qty = 100, 209 | stop = Linea_C, 210 | limit = Linea_C) 211 | 212 | if Long5 213 | strategy.entry(id = "LONG5", 214 | direction = strategy.long, 215 | qty = 1) 216 | if isExit5 217 | strategy.exit(id = "EXIT5", 218 | from_entry = "LONG5", 219 | qty = 100, 220 | stop = Linea_D, 221 | limit = Linea_D) 222 | 223 | if Long6 224 | strategy.entry(id = "LONG6", 225 | direction = strategy.long, 226 | qty = 1) 227 | if isExit6 228 | strategy.exit(id = "EXIT6", 229 | from_entry = "LONG6", 230 | qty = 100, 231 | stop = Linea_50Percent, 232 | limit = Linea_50Percent) 233 | 234 | if Long7 235 | strategy.entry(id = "LONG7", 236 | direction = strategy.long, 237 | qty = 1) 238 | if isExit7 239 | strategy.exit(id = "EXIT7", 240 | from_entry = "LONG7", 241 | qty = 100, 242 | stop = Linea_E, 243 | limit = Linea_E) 244 | 245 | if Long8 246 | strategy.entry(id = "LONG8", 247 | direction = strategy.long, 248 | qty = 1) 249 | if isExit8 250 | strategy.exit(id = "EXIT8", 251 | from_entry = "LONG8", 252 | qty = 100, 253 | stop = Linea_F, 254 | limit = Linea_F) 255 | 256 | if Long9 257 | strategy.entry(id = "LONG9", 258 | direction = strategy.long, 259 | qty = 1) 260 | if isExit9 261 | strategy.exit(id = "EXIT9", 262 | from_entry = "LONG9", 263 | qty = 100, 264 | stop = Linea_G, 265 | limit = Linea_G) 266 | 267 | if Long10 268 | strategy.entry(id = "LONG10", 269 | direction = strategy.long, 270 | qty = 1) 271 | if isExit10 272 | strategy.exit(id = "EXIT10", 273 | from_entry = "LONG10", 274 | qty = 100, 275 | stop = Linea_H, 276 | limit = Linea_H) 277 | 278 | //--------------------------------------------------------------- 279 | 280 | trades = int(strategy.closedtrades) 281 | initialCapital = float(strategy.initial_capital) 282 | grossReturn = trades * PercentProfit 283 | gainCapital = (initialCapital * grossReturn) / 100 284 | totalCapital = initialCapital + gainCapital 285 | feeCost = (totalCapital * feePerTrade) / 100 286 | totalFeeCost = feeCost * trades 287 | netCapital = totalCapital - totalFeeCost - initialCapital 288 | 289 | //--------------------------------------------------------------- 290 | 291 | i_position = input.string(defval = "Bottom Right", 292 | title = "Table Placement", 293 | options = ["Top Right", "Middle Right", "Bottom Right"], 294 | group = "3. Table Characteristics") 295 | 296 | position = i_position == "Top Right" ? position.top_right : i_position == "Middle Right" ? position.middle_right : position.bottom_right 297 | 298 | i_w = input.int(title = "Width", 299 | defval = 14, 300 | group = "3. Table Characteristics") 301 | i_h = input.int(title = "Height", 302 | defval = 8, 303 | group = "3. Table Characteristics") 304 | i_text_size = input(title = "Text Size", 305 | defval = "Normal") 306 | 307 | text_size = i_text_size == "Normal" ? size.normal : i_text_size == "Auto" ? size.auto : i_text_size == "Auto" ? size.auto : i_text_size == "Tiny" ? size.tiny 308 | : i_text_size == "Small" ? size.small : i_text_size == "Large" ? size.large : size.huge 309 | 310 | i = input.bool(title = "1. Capital", 311 | defval = true, 312 | group = "4. Enable Rows", 313 | tooltip = "Initial capital of the strategy.") 314 | 315 | i_1 = input.bool(title = "2. Gross Returns", 316 | defval = true, 317 | group = "4. Enable Rows", 318 | tooltip = "Total strategy return. Contains the initial capital plus the sum of the gains.") 319 | 320 | i_2 = input.bool(title = "3. Gain Percent", 321 | defval = true, 322 | group = "4. Enable Rows", 323 | tooltip = "Percentage return of strategy gains.") 324 | 325 | i_3 = input.bool(title = "4. Commission Cost", 326 | defval = true, 327 | group = "4. Enable Rows", 328 | tooltip = "Percentage return of strategy gains.") 329 | 330 | i_4 = input.bool(title = "5. Net Returns", 331 | defval = true, 332 | group = "4. Enable Rows", 333 | tooltip = "Total commission cost.") 334 | 335 | i_5 = input.bool(title = "6. Closed Trades", 336 | defval = true, 337 | group = "4. Enable Rows", 338 | tooltip = "Total trades.") 339 | 340 | 341 | var table perfTable = table.new(position, 2, 11, frame_color = color.purple, frame_width = 1, border_width = 1) 342 | 343 | //--------------------------------------------------------------- 344 | 345 | if i 346 | table.cell(perfTable, 0, 0, "1. GROSS RETURN", 347 | bgcolor = color.new(color.purple, 90), 348 | text_color = color.new(color.white, 0), 349 | width = i_w, 350 | height = i_h, 351 | text_size = text_size, 352 | text_halign = text.align_left) 353 | if i 354 | table.cell(perfTable, 1, 0, 355 | text = str.tostring(totalCapital) + " $", 356 | bgcolor = color.new(color.purple, 90), 357 | text_color = color.new(color.white, 0), 358 | width = i_w, 359 | height = i_h, 360 | text_size = text_size) 361 | 362 | if i_1 363 | table.cell(perfTable, 0, 1, "2. INITIAL CAPITAL", 364 | bgcolor = color.new(color.purple, 90), 365 | text_color = color.new(color.white, 0), 366 | width = i_w, 367 | height = i_h, 368 | text_size = text_size, 369 | text_halign = text.align_left) 370 | if i_1 371 | table.cell(perfTable, 1, 1, text = str.tostring(initialCapital) + " $", 372 | bgcolor = color.new(color.purple, 90), 373 | text_color = color.new(color.white, 0), 374 | width = i_w, 375 | height = i_h, 376 | text_size = text_size) 377 | 378 | if i_2 379 | table.cell(perfTable, 0, 2, "3. GAIN", 380 | bgcolor = color.new(color.purple, 90), 381 | text_color = color.new(color.white, 0), 382 | width = i_w, 383 | height = i_h, 384 | text_size = text_size, 385 | text_halign = text.align_left) 386 | if i_2 387 | table.cell(perfTable, 1, 2, 388 | text = str.tostring(grossReturn) + " %", 389 | bgcolor = color.new(color.purple, 90), 390 | text_color = color.new(color.white, 0), 391 | width = i_w, 392 | height = i_h, 393 | text_size = text_size) 394 | 395 | if i_3 396 | table.cell(perfTable, 0, 3, "4. FEE COST", 397 | bgcolor = color.new(color.purple, 90), 398 | text_color = color.new(color.white, 0), 399 | width = i_w, 400 | height = i_h, 401 | text_size = text_size, 402 | text_halign = text.align_left) 403 | if i_3 404 | table.cell(perfTable, 1, 3, 405 | text = str.tostring(totalFeeCost) + " $", 406 | bgcolor = color.new(color.purple, 90), 407 | text_color = color.new(color.white, 0), 408 | width = i_w, 409 | height = i_h, 410 | text_size = text_size) 411 | 412 | if i_4 413 | table.cell(perfTable, 0, 4, "5. NET RETURN", 414 | bgcolor = color.new(color.purple, 90), 415 | text_color = color.new(color.white, 0), 416 | width = i_w, 417 | height = i_h, 418 | text_size = text_size, 419 | text_halign = text.align_left) 420 | if i_4 421 | table.cell(perfTable, 1, 4, 422 | text = str.tostring(netCapital) + " $", 423 | bgcolor = color.new(color.purple, 90), 424 | text_color = color.new(color.white, 0), 425 | width = i_w, 426 | height = i_h, 427 | text_size = text_size) 428 | 429 | if i_5 430 | table.cell(perfTable, 0, 6, "6. CLOSED TRADES", 431 | bgcolor = color.new(color.purple, 90), 432 | text_color = color.new(color.white, 0), 433 | width = i_w, 434 | height = i_h, 435 | text_size = text_size, 436 | text_halign = text.align_left) 437 | if i_5 438 | table.cell(perfTable, 1, 6, 439 | text = str.tostring(trades), 440 | bgcolor = color.new(color.purple, 90), 441 | text_color = color.new(color.white, 0), 442 | width = i_w, 443 | height = i_h, 444 | text_size = text_size) 445 | 446 | //--------------------------------------------------------------- 447 | --------------------------------------------------------------------------------