code.vegaprotocol.io/vega@v0.79.0/core/execution/stoporders/trailing_stop_orders_test.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package stoporders_test 17 18 import ( 19 "testing" 20 21 "code.vegaprotocol.io/vega/core/execution/stoporders" 22 "code.vegaprotocol.io/vega/core/types" 23 "code.vegaprotocol.io/vega/libs/num" 24 25 "github.com/stretchr/testify/assert" 26 ) 27 28 func TestTrailingRemoveOrderRisesAbove(t *testing.T) { 29 trailing := stoporders.NewTrailingStopOrders() 30 31 // initial price 32 trailing.PriceUpdated(num.NewUint(50)) 33 trailing.Insert("a", num.DecimalFromFloat(0.50), types.StopOrderTriggerDirectionRisesAbove) 34 35 trailing.PriceUpdated(num.NewUint(60)) 36 trailing.Insert("b", num.DecimalFromFloat(0.10), types.StopOrderTriggerDirectionRisesAbove) 37 38 atPrice, offset, ok := trailing.Exists("a") 39 assert.True(t, ok) 40 assert.Equal(t, atPrice, num.NewUint(50)) 41 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 42 43 atPrice, offset, ok = trailing.Exists("b") 44 assert.True(t, ok) 45 assert.Equal(t, atPrice, num.NewUint(60)) 46 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 47 48 affectedOrders := trailing.PriceUpdated(num.NewUint(55)) 49 assert.Len(t, affectedOrders, 0) 50 51 atPrice, offset, ok = trailing.Exists("a") 52 assert.True(t, ok) 53 assert.Equal(t, atPrice, num.NewUint(50)) 54 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 55 56 atPrice, offset, ok = trailing.Exists("b") 57 assert.True(t, ok) 58 assert.Equal(t, atPrice, num.NewUint(55)) 59 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 60 61 // should have 2 in the 55 bucket 62 trailing.Insert("c", num.DecimalFromFloat(0.15), types.StopOrderTriggerDirectionRisesAbove) 63 64 // now remove stuff 65 // 2 levels 66 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionRisesAbove), 2) 67 68 err := trailing.Remove("b") 69 assert.NoError(t, err) 70 _, _, ok = trailing.Exists("b") 71 assert.False(t, ok) 72 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionRisesAbove), 2) 73 err = trailing.Remove("b") 74 assert.EqualError(t, err, "order not found") 75 76 err = trailing.Remove("a") 77 assert.NoError(t, err) 78 _, _, ok = trailing.Exists("a") 79 assert.False(t, ok) 80 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionRisesAbove), 1) 81 err = trailing.Remove("a") 82 assert.EqualError(t, err, "order not found") 83 84 err = trailing.Remove("c") 85 assert.NoError(t, err) 86 _, _, ok = trailing.Exists("c") 87 assert.False(t, ok) 88 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionRisesAbove), 0) 89 err = trailing.Remove("c") 90 assert.EqualError(t, err, "order not found") 91 } 92 93 func TestTrailingRemoveOrderFallsBelow(t *testing.T) { 94 trailing := stoporders.NewTrailingStopOrders() 95 96 // initial price 97 trailing.PriceUpdated(num.NewUint(50)) 98 trailing.Insert("a", num.DecimalFromFloat(0.50), types.StopOrderTriggerDirectionFallsBelow) 99 100 trailing.PriceUpdated(num.NewUint(40)) 101 trailing.Insert("b", num.DecimalFromFloat(0.10), types.StopOrderTriggerDirectionFallsBelow) 102 103 atPrice, offset, ok := trailing.Exists("a") 104 assert.True(t, ok) 105 assert.Equal(t, atPrice, num.NewUint(50)) 106 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 107 108 atPrice, offset, ok = trailing.Exists("b") 109 assert.True(t, ok) 110 assert.Equal(t, atPrice, num.NewUint(40)) 111 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 112 113 affectedOrders := trailing.PriceUpdated(num.NewUint(45)) 114 assert.Len(t, affectedOrders, 0) 115 116 atPrice, offset, ok = trailing.Exists("a") 117 assert.True(t, ok) 118 assert.Equal(t, atPrice, num.NewUint(50)) 119 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 120 121 atPrice, offset, ok = trailing.Exists("b") 122 assert.True(t, ok) 123 assert.Equal(t, atPrice, num.NewUint(45)) 124 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 125 126 // should have 2 in the 55 bucket 127 trailing.Insert("c", num.DecimalFromFloat(0.15), types.StopOrderTriggerDirectionFallsBelow) 128 129 // now remove stuff 130 // 2 levels 131 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionFallsBelow), 2) 132 133 err := trailing.Remove("b") 134 assert.NoError(t, err) 135 _, _, ok = trailing.Exists("b") 136 assert.False(t, ok) 137 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionFallsBelow), 2) 138 err = trailing.Remove("b") 139 assert.EqualError(t, err, "order not found") 140 141 err = trailing.Remove("a") 142 assert.NoError(t, err) 143 _, _, ok = trailing.Exists("a") 144 assert.False(t, ok) 145 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionFallsBelow), 1) 146 err = trailing.Remove("a") 147 assert.EqualError(t, err, "order not found") 148 149 err = trailing.Remove("c") 150 assert.NoError(t, err) 151 _, _, ok = trailing.Exists("c") 152 assert.False(t, ok) 153 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionFallsBelow), 0) 154 err = trailing.Remove("c") 155 assert.EqualError(t, err, "order not found") 156 } 157 158 // - A trailing stop order for a 5% drop placed when the price is `50`, followed by a 159 // price rise to `60` will: 160 // - Be triggered by a fall to `57`. (<a name="0014-ORDT-027" 161 // 162 // href="#0014-ORDT-027">0014-ORDT-027</a>) 163 // - Not be triggered by a fall to `58`. (<a name="0014-ORDT-036" 164 // 165 // href="#0014-ORDT-036">0014-ORDT-036</a>). 166 func TestTrailingAC_0014_ORDT_027_036(t *testing.T) { 167 trailing := stoporders.NewTrailingStopOrders() 168 169 // initial price 170 trailing.PriceUpdated(num.NewUint(50)) 171 trailing.Insert("a", num.DecimalFromFloat(0.05), types.StopOrderTriggerDirectionFallsBelow) 172 173 // price move 174 trailing.PriceUpdated(num.NewUint(60)) 175 176 atPrice, offset, ok := trailing.Exists("a") 177 assert.True(t, ok) 178 assert.Equal(t, atPrice, num.NewUint(60)) 179 assert.Equal(t, offset, num.DecimalFromFloat(0.05)) 180 181 // move price to 58, nothing happen 182 affectedOrders := trailing.PriceUpdated(num.NewUint(58)) 183 assert.Len(t, affectedOrders, 0) 184 185 affectedOrders = trailing.PriceUpdated(num.NewUint(57)) 186 assert.Len(t, affectedOrders, 1) 187 188 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionFallsBelow), 0) 189 } 190 191 // A trailing stop order for a 5% rise placed when the price is `50`, followed by a drop 192 // to `40` will: 193 // - Be triggered by a rise to `42`. (<a name="0014-ORDT-028" 194 // 195 // href="#0014-ORDT-028">0014-ORDT-028</a>) 196 // - Not be triggered by a rise to `41`. (<a name="0014-ORDT-029" 197 // 198 // href="#0014-ORDT-029">0014-ORDT-029</a>). 199 func TestTrailingAC_0014_ORDT_827_029(t *testing.T) { 200 trailing := stoporders.NewTrailingStopOrders() 201 202 // initial price 203 trailing.PriceUpdated(num.NewUint(50)) 204 trailing.Insert("a", num.DecimalFromFloat(0.05), types.StopOrderTriggerDirectionRisesAbove) 205 206 // price move 207 trailing.PriceUpdated(num.NewUint(40)) 208 209 atPrice, offset, ok := trailing.Exists("a") 210 assert.True(t, ok) 211 assert.Equal(t, atPrice, num.NewUint(40)) 212 assert.Equal(t, offset, num.DecimalFromFloat(0.05)) 213 214 // move price to 41, nothing happen 215 affectedOrders := trailing.PriceUpdated(num.NewUint(41)) 216 assert.Len(t, affectedOrders, 0) 217 218 affectedOrders = trailing.PriceUpdated(num.NewUint(42)) 219 assert.Len(t, affectedOrders, 1) 220 221 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionRisesAbove), 0) 222 } 223 224 // A trailing stop order for a 25% drop placed when the price is `50`, followed by a 225 // price rise to `60`, then to `50`, then another rise to `57` will: 226 // - Be triggered by a fall to `45`. (<a name="0014-ORDT-030" 227 // 228 // href="#0014-ORDT-030">0014-ORDT-030</a>) 229 // - Not be triggered by a fall to `46`. (<a name="0014-ORDT-031" 230 // 231 // href="#0014-ORDT-031">0014-ORDT-031</a>). 232 func TestTrailingAC_0014_ORDT_030_031(t *testing.T) { 233 trailing := stoporders.NewTrailingStopOrders() 234 235 // initial price 236 trailing.PriceUpdated(num.NewUint(50)) 237 trailing.Insert("a", num.DecimalFromFloat(0.25), types.StopOrderTriggerDirectionFallsBelow) 238 239 // price move 240 affectedOrders := trailing.PriceUpdated(num.NewUint(60)) 241 assert.Len(t, affectedOrders, 0) 242 243 affectedOrders = trailing.PriceUpdated(num.NewUint(50)) 244 assert.Len(t, affectedOrders, 0) 245 246 affectedOrders = trailing.PriceUpdated(num.NewUint(57)) 247 assert.Len(t, affectedOrders, 0) 248 249 atPrice, offset, ok := trailing.Exists("a") 250 assert.True(t, ok) 251 assert.Equal(t, atPrice, num.NewUint(60)) 252 assert.Equal(t, offset, num.DecimalFromFloat(0.25)) 253 254 // move price to 46, nothing happen 255 affectedOrders = trailing.PriceUpdated(num.NewUint(46)) 256 assert.Len(t, affectedOrders, 0) 257 258 affectedOrders = trailing.PriceUpdated(num.NewUint(45)) 259 assert.Len(t, affectedOrders, 1) 260 261 assert.Equal(t, trailing.Len(types.StopOrderTriggerDirectionFallsBelow), 0) 262 } 263 264 func TestTrailingStopOrdersMultipleOffsetPerPriceFallsBelow(t *testing.T) { 265 trailing := stoporders.NewTrailingStopOrders() 266 267 // initial price 268 trailing.PriceUpdated(num.NewUint(50)) 269 // won't trigger unless it goes bellow 50% 270 trailing.Insert("a", num.DecimalFromFloat(0.50), types.StopOrderTriggerDirectionFallsBelow) 271 272 trailing.PriceUpdated(num.NewUint(40)) 273 // won't trigger unless it goes bellow 50% 274 trailing.Insert("b", num.DecimalFromFloat(0.10), types.StopOrderTriggerDirectionFallsBelow) 275 276 // as of no they should be in 2 differen buckets 277 278 atPrice, offset, ok := trailing.Exists("a") 279 assert.True(t, ok) 280 assert.Equal(t, atPrice, num.NewUint(50)) 281 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 282 283 atPrice, offset, ok = trailing.Exists("b") 284 assert.True(t, ok) 285 assert.Equal(t, atPrice, num.NewUint(40)) 286 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 287 288 affectedOrders := trailing.PriceUpdated(num.NewUint(45)) 289 assert.Len(t, affectedOrders, 0) 290 291 // ensure a is still in the same bucked 292 // b moved to 45 293 294 atPrice, offset, ok = trailing.Exists("a") 295 assert.True(t, ok) 296 assert.Equal(t, atPrice, num.NewUint(50)) 297 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 298 299 atPrice, offset, ok = trailing.Exists("b") 300 assert.True(t, ok) 301 assert.Equal(t, atPrice, num.NewUint(45)) 302 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 303 304 affectedOrders = trailing.PriceUpdated(num.NewUint(60)) 305 assert.Len(t, affectedOrders, 0) 306 307 // ensure they are in the same buckets 308 309 atPrice, offset, ok = trailing.Exists("a") 310 assert.True(t, ok) 311 assert.Equal(t, atPrice, num.NewUint(60)) 312 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 313 314 atPrice, offset, ok = trailing.Exists("b") 315 assert.True(t, ok) 316 assert.Equal(t, atPrice, num.NewUint(60)) 317 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 318 319 // now move prices so b triggers 320 affectedOrders = trailing.PriceUpdated(num.NewUint(54)) 321 assert.Len(t, affectedOrders, 1) 322 assert.Equal(t, affectedOrders[0], "b") 323 324 atPrice, offset, ok = trailing.Exists("a") 325 assert.True(t, ok) 326 assert.Equal(t, atPrice, num.NewUint(60)) 327 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 328 329 _, _, ok = trailing.Exists("b") 330 assert.False(t, ok) 331 } 332 333 func TestTrailingStopOrdersMultipleOffsetPerPriceRisesAbove(t *testing.T) { 334 trailing := stoporders.NewTrailingStopOrders() 335 336 // initial price 337 trailing.PriceUpdated(num.NewUint(50)) 338 // won't trigger unless it goes bellow 50% 339 trailing.Insert("a", num.DecimalFromFloat(0.50), types.StopOrderTriggerDirectionRisesAbove) 340 341 trailing.PriceUpdated(num.NewUint(60)) 342 // won't trigger unless it goes bellow 50% 343 trailing.Insert("b", num.DecimalFromFloat(0.10), types.StopOrderTriggerDirectionRisesAbove) 344 345 // as of no they should be in 2 differen buckets 346 347 atPrice, offset, ok := trailing.Exists("a") 348 assert.True(t, ok) 349 assert.Equal(t, atPrice, num.NewUint(50)) 350 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 351 352 atPrice, offset, ok = trailing.Exists("b") 353 assert.True(t, ok) 354 assert.Equal(t, atPrice, num.NewUint(60)) 355 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 356 357 affectedOrders := trailing.PriceUpdated(num.NewUint(55)) 358 assert.Len(t, affectedOrders, 0) 359 360 // ensure a is still in the same bucked 361 // b moved to 45 362 363 atPrice, offset, ok = trailing.Exists("a") 364 assert.True(t, ok) 365 assert.Equal(t, atPrice, num.NewUint(50)) 366 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 367 368 atPrice, offset, ok = trailing.Exists("b") 369 assert.True(t, ok) 370 assert.Equal(t, atPrice, num.NewUint(55)) 371 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 372 373 affectedOrders = trailing.PriceUpdated(num.NewUint(40)) 374 assert.Len(t, affectedOrders, 0) 375 376 // ensure they are in the same buckets 377 378 atPrice, offset, ok = trailing.Exists("a") 379 assert.True(t, ok) 380 assert.Equal(t, atPrice, num.NewUint(40)) 381 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 382 383 atPrice, offset, ok = trailing.Exists("b") 384 assert.True(t, ok) 385 assert.Equal(t, atPrice, num.NewUint(40)) 386 assert.Equal(t, offset, num.DecimalFromFloat(0.10)) 387 388 // now move prices so b triggers 389 affectedOrders = trailing.PriceUpdated(num.NewUint(44)) 390 assert.Len(t, affectedOrders, 1) 391 assert.Equal(t, affectedOrders[0], "b") 392 393 atPrice, offset, ok = trailing.Exists("a") 394 assert.True(t, ok) 395 assert.Equal(t, atPrice, num.NewUint(40)) 396 assert.Equal(t, offset, num.DecimalFromFloat(0.50)) 397 398 _, _, ok = trailing.Exists("b") 399 assert.False(t, ok) 400 }