code.vegaprotocol.io/vega@v0.79.0/commands/stop_orders_submission.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 commands 17 18 import ( 19 "fmt" 20 "math/big" 21 22 "code.vegaprotocol.io/vega/libs/num" 23 types "code.vegaprotocol.io/vega/protos/vega" 24 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 25 ) 26 27 func CheckStopOrdersSubmission(cmd *commandspb.StopOrdersSubmission) error { 28 return checkStopOrdersSubmission(cmd).ErrorOrNil() 29 } 30 31 func checkStopOrdersSubmission(cmd *commandspb.StopOrdersSubmission) Errors { 32 errs := NewErrors() 33 34 if cmd == nil { 35 return errs.FinalAddForProperty("stop_orders_submission", ErrIsRequired) 36 } 37 38 var market1, market2 string 39 if cmd.FallsBelow != nil { 40 checkStopOrderSetup( 41 "stop_orders_submission.falls_below", errs, cmd.FallsBelow, cmd.RisesAbove != nil) 42 if cmd.FallsBelow.OrderSubmission != nil { 43 market1 = cmd.FallsBelow.OrderSubmission.MarketId 44 if cmd.FallsBelow.SizeOverrideSetting != nil { 45 if *cmd.FallsBelow.SizeOverrideSetting == types.StopOrder_SIZE_OVERRIDE_SETTING_POSITION { 46 if cmd.FallsBelow.SizeOverrideValue != nil { 47 value := cmd.FallsBelow.SizeOverrideValue.GetPercentage() 48 // Check that the string represents a number between >0&&>=1 49 percentage, err := num.DecimalFromString(value) 50 if err != nil { 51 return errs.FinalAddForProperty("stop_orders_submission.falls_below.size_override_value", ErrIsNotValidNumber) 52 } 53 if percentage.LessThanOrEqual(num.DecimalFromFloat(0.0)) { 54 return errs.FinalAddForProperty("stop_orders_submission.falls_below.size_override_value", ErrMustBeBetween01) 55 } 56 if percentage.GreaterThan(num.DecimalFromFloat(1.0)) { 57 return errs.FinalAddForProperty("stop_orders_submission.falls_below.size_override_value", ErrMustBeBetween01) 58 } 59 } 60 } 61 } 62 } 63 } 64 65 if cmd.RisesAbove != nil { 66 checkStopOrderSetup( 67 "stop_orders_submission.rises_below", errs, cmd.RisesAbove, cmd.FallsBelow != nil) 68 if cmd.RisesAbove.OrderSubmission != nil { 69 market2 = cmd.RisesAbove.OrderSubmission.MarketId 70 if cmd.RisesAbove.SizeOverrideSetting != nil { 71 if *cmd.RisesAbove.SizeOverrideSetting == types.StopOrder_SIZE_OVERRIDE_SETTING_POSITION { 72 if cmd.RisesAbove.SizeOverrideValue != nil { 73 value := cmd.RisesAbove.SizeOverrideValue.GetPercentage() 74 // Check that the string represents a number between >0&&>=1 75 percentage, err := num.DecimalFromString(value) 76 if err != nil { 77 return errs.FinalAddForProperty("stop_orders_submission.rises_above.size_override_value", ErrIsNotValidNumber) 78 } 79 if percentage.LessThanOrEqual(num.DecimalFromFloat(0.0)) { 80 return errs.FinalAddForProperty("stop_orders_submission.rises_above.size_override_value", ErrMustBeBetween01) 81 } 82 if percentage.GreaterThan(num.DecimalFromFloat(1.0)) { 83 return errs.FinalAddForProperty("stop_orders_submission.rises_above.size_override_value", ErrMustBeBetween01) 84 } 85 } 86 } 87 } 88 } 89 } 90 91 if cmd.FallsBelow == nil && cmd.RisesAbove == nil { 92 return errs.FinalAdd(ErrMustHaveAtLeastOneOfRisesAboveOrFallsBelow) 93 } 94 95 if cmd.FallsBelow != nil && cmd.RisesAbove != nil && market1 != market2 { 96 return errs.FinalAdd(ErrFallsBelowAndRiseAboveMarketIDMustBeTheSame) 97 } 98 99 return errs 100 } 101 102 func checkStopOrderSetup( 103 fieldName string, 104 errs Errors, 105 setup *commandspb.StopOrderSetup, 106 isOCO bool, 107 ) { 108 if err := checkOrderSubmission(setup.OrderSubmission); !err.Empty() { 109 errs.Merge(err) 110 } 111 112 if setup.OrderSubmission != nil && setup.OrderSubmission.TimeInForce == types.Order_TIME_IN_FORCE_GFA { 113 errs.AddForProperty(fmt.Sprintf("%s.order_submission.time_in_force", fieldName), ErrIsNotValid) 114 } 115 116 if setup.ExpiresAt != nil { 117 if *setup.ExpiresAt < 0 { 118 errs.AddForProperty(fmt.Sprintf("%s.expires_at", fieldName), ErrMustBePositive) 119 } 120 if setup.ExpiryStrategy == nil { 121 errs.AddForProperty(fmt.Sprintf("%s.expiry_strategy", fieldName), ErrExpiryStrategyRequiredWhenExpiresAtSet) 122 } else { 123 if *setup.ExpiryStrategy == types.StopOrder_EXPIRY_STRATEGY_UNSPECIFIED { 124 errs.AddForProperty(fmt.Sprintf("%s.expiry_strategy", fieldName), ErrIsRequired) 125 } else if _, ok := types.StopOrder_ExpiryStrategy_name[int32(*setup.ExpiryStrategy)]; !ok { 126 errs.AddForProperty(fmt.Sprintf("%s.expiry_strategy", fieldName), ErrIsNotValid) 127 } else if isOCO && *setup.ExpiryStrategy == types.StopOrder_EXPIRY_STRATEGY_SUBMIT { 128 errs.AddForProperty(fmt.Sprintf("%s.expiry_strategy", fieldName), ErrIsNotValidWithOCO) 129 } 130 } 131 } 132 133 if setup.Trigger != nil { 134 switch t := setup.Trigger.(type) { 135 case *commandspb.StopOrderSetup_Price: 136 if len(t.Price) <= 0 { 137 errs.AddForProperty(fmt.Sprintf("%s.trigger.price", fieldName), ErrIsRequired) 138 } else { 139 if price, ok := big.NewInt(0).SetString(t.Price, 10); !ok { 140 errs.AddForProperty(fmt.Sprintf("%s.trigger.price", fieldName), ErrNotAValidInteger) 141 } else if price.Cmp(big.NewInt(0)) != 1 { 142 errs.AddForProperty(fmt.Sprintf("%s.trigger.price", fieldName), ErrMustBePositive) 143 } 144 } 145 case *commandspb.StopOrderSetup_TrailingPercentOffset: 146 if len(t.TrailingPercentOffset) <= 0 { 147 errs.AddForProperty(fmt.Sprintf("%s.trigger.trailing_percent_offset", fieldName), ErrIsRequired) 148 } else { 149 if poffset, err := num.DecimalFromString(t.TrailingPercentOffset); err != nil { 150 errs.AddForProperty(fmt.Sprintf("%s.trigger.trailing_percent_offset", fieldName), ErrNotAValidFloat) 151 } else if poffset.LessThanOrEqual(num.DecimalZero()) || poffset.GreaterThanOrEqual(num.DecimalOne()) { 152 errs.AddForProperty(fmt.Sprintf("%s.trigger.trailing_percent_offset", fieldName), ErrMustBeWithinRange01) 153 } else if !poffset.Mod(num.MustDecimalFromString("0.001")).Equal(num.DecimalZero()) { 154 errs.AddForProperty(fmt.Sprintf("%s.trigger.trailing_percent_offset", fieldName), ErrTrailingPercentOffsetMinimalIncrementNotReached) 155 } 156 } 157 } 158 } else { 159 errs.AddForProperty(fmt.Sprintf("%s.trigger", fieldName), ErrMustHaveAStopOrderTrigger) 160 } 161 }