Skip to content

Commit

Permalink
Merge pull request #10341 from vegaprotocol/517-0014-ORDT-130
Browse files Browse the repository at this point in the history
Adding AC for 0014-ORDT-130
  • Loading branch information
EVODelavega authored Jan 11, 2024
2 parents 0320fec + c6ccf9c commit 5a67c99
Show file tree
Hide file tree
Showing 12 changed files with 1,738 additions and 1,627 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ all: build

.PHONY: lint
lint: ## Lint the files
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.2 run -v --config .golangci.toml
go run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 run -v --config .golangci.toml

.PHONY: retest
retest: ## Re-run all unit tests
Expand Down
2 changes: 2 additions & 0 deletions core/execution/common/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,6 @@ var (
ErrPartyHasNoExistingLiquidityProvision = errors.New("party has no existing liquidity provision")
// ErrStopOrderNotAllowedDuringOpeningAuction is returned if a trader attempts to send a stop order to a market that is in opening auction.
ErrStopOrderNotAllowedDuringOpeningAuction = errors.New("stop orders are not accepted during the opening auction")
// ErrStopOrderNotAllowedSameExpiryTimeForOCO is returned if both sides of an OCO have the same expiry time.
ErrStopOrderNotAllowedSameExpiry = errors.New("stop order OCOs must not have the same expiry time")
)
7 changes: 7 additions & 0 deletions core/execution/future/market.go
Original file line number Diff line number Diff line change
Expand Up @@ -1871,6 +1871,13 @@ func (m *Market) SubmitStopOrdersWithIDGeneratorAndOrderIDs(
orderCnt++
}

if risesAbove != nil && fallsBelow != nil {
if risesAbove.Expiry.Expires() && fallsBelow.Expiry.Expires() && risesAbove.Expiry.ExpiresAt.Compare(*fallsBelow.Expiry.ExpiresAt) == 0 {
rejectStopOrders(types.StopOrderRejectionOCONotAllowedSameExpiryTime, fallsBelow, risesAbove)
return nil, common.ErrStopOrderNotAllowedSameExpiry
}
}

// now check if that party hasn't exceeded the max amount per market
if m.stopOrders.CountForParty(party)+uint64(orderCnt) > m.maxStopOrdersPerParties.Uint64() {
rejectStopOrders(types.StopOrderRejectionMaxStopOrdersPerPartyReached, fallsBelow, risesAbove)
Expand Down
65 changes: 59 additions & 6 deletions core/integration/features/orders/stoporders.feature
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ Feature: stop orders
When time is updated to "2019-11-30T00:00:10Z"
# create party1 stop order, no trade resulting, expires in 10 secs
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | reference | so expires in | so expiry strategy |
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | reference | ra expires in | ra expiry strategy |
| party1 | ETH/DEC19 | buy | 10 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | | stop1 | 10 | EXPIRY_STRATEGY_CANCELS |

# add 20 secs, should expire
Expand Down Expand Up @@ -617,7 +617,7 @@ Feature: stop orders
When time is updated to "2019-11-30T00:00:10Z"
# create party1 stop order, no trade resulting, expires in 10 secs
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | reference | so expires in | so expiry strategy |
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | reference | ra expires in | ra expiry strategy |
| party1 | ETH/DEC19 | buy | 10 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | | stop1 | 10 | EXPIRY_STRATEGY_SUBMIT |

# add 20 secs, should expire
Expand Down Expand Up @@ -679,7 +679,7 @@ Feature: stop orders
| party3 | ETH/DEC19 | buy | 20 | 20 | 0 | TYPE_LIMIT | TIF_GTC |
# create party1 stop order, no trade resulting, expires in 10 secs
And the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | only | fb price trigger | error | reference | so expires in | so expiry strategy |
| party | market id | side | volume | price | resulting trades | type | tif | only | fb price trigger | error | reference | fb expires in | fb expiry strategy |
| party1 | ETH/DEC19 | sell | 10 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 25 | | stop1 | 10 | EXPIRY_STRATEGY_SUBMIT |

# trigger the stop order
Expand Down Expand Up @@ -1620,7 +1620,7 @@ Feature: stop orders
| party2 | ETH/DEC20 | sell | 1 | 5000 | 1 | TYPE_LIMIT | TIF_GTC |
# Place a stop order which will expire during the auction
And the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | so expires in | so expiry strategy | reference |
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | ra expires in | ra expiry strategy | reference |
| party1 | ETH/DEC20 | sell | 1 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 5020 | | 5 | EXPIRY_STRATEGY_SUBMIT | stop |
# Trigger a price-monitoring auction
When the parties place the following orders:
Expand Down Expand Up @@ -1706,7 +1706,7 @@ Feature: stop orders
| party3 | ETH/DEC19 | buy | 20 | 20 | 0 | TYPE_LIMIT | TIF_GTC |
# create party1 stop order, no trade resulting, expires in 10 secs
And the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | only | fb price trigger | ra price trigger | error | reference | so expires in | so expiry strategy |
| party | market id | side | volume | price | resulting trades | type | tif | only | fb price trigger | ra price trigger | error | reference | fb expires in | fb expiry strategy |
| party1 | ETH/DEC19 | sell | 10 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 25 | 100 | | stop1 | 10 | EXPIRY_STRATEGY_SUBMIT |

# trigger the stop order
Expand Down Expand Up @@ -1777,9 +1777,62 @@ Feature: stop orders
When time is updated to "2019-11-30T00:00:10Z"
# create party1 stop order, no trade resulting, expires in 10 secs
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | reference | so expires in | so expiry strategy |
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | error | reference | ra expires in | ra expiry strategy |
| party1 | ETH/DEC19 | buy | 10 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | stop order expiry in the past | stop1 | -10 | EXPIRY_STRATEGY_SUBMIT |

Then the stop orders should have the following states
| party | market id | status | reference |
| party1 | ETH/DEC19 | STATUS_REJECTED | stop1 |



Scenario: An OCO stop order with expiration time T with both sides set to execute at that time will be rejected on submission (0014-ORDT-130)

# setup accounts
Given time is updated to "2019-11-30T00:00:00Z"
Given the parties deposit on asset's general account the following amount:
| party | asset | amount |
| party1 | BTC | 10000 |
| party2 | BTC | 10000 |
| party3 | BTC | 10000 |
| aux | BTC | 100000 |
| aux2 | BTC | 100000 |
| aux3 | BTC | 100000 |
| lpprov | BTC | 90000000 |

When the parties submit the following liquidity provision:
| id | party | market id | commitment amount | fee | lp type |
| lp1 | lpprov | ETH/DEC19 | 90000000 | 0.1 | submission |
| lp1 | lpprov | ETH/DEC19 | 90000000 | 0.1 | submission |
And the parties place the following pegged iceberg orders:
| party | market id | peak size | minimum visible size | side | pegged reference | volume | offset |
| lpprov | ETH/DEC19 | 2 | 1 | buy | BID | 50 | 100 |
| lpprov | ETH/DEC19 | 2 | 1 | sell | ASK | 50 | 100 |

# place auxiliary orders so we always have best bid and best offer as to not trigger the liquidity auction
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| aux | ETH/DEC19 | buy | 1 | 1 | 0 | TYPE_LIMIT | TIF_GTC |
| aux | ETH/DEC19 | sell | 1 | 10001 | 0 | TYPE_LIMIT | TIF_GTC |
| aux2 | ETH/DEC19 | buy | 5 | 50 | 0 | TYPE_LIMIT | TIF_GTC |
| aux3 | ETH/DEC19 | sell | 5 | 50 | 0 | TYPE_LIMIT | TIF_GTC |

Then the opening auction period ends for market "ETH/DEC19"
And the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH/DEC19"

When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| party1 | ETH/DEC19 | sell | 10 | 50 | 0 | TYPE_LIMIT | TIF_GTC |
| party2 | ETH/DEC19 | buy | 10 | 50 | 1 | TYPE_LIMIT | TIF_GTC |

# volume for the stop trade
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif |
| party3 | ETH/DEC19 | sell | 10 | 50 | 0 | TYPE_LIMIT | TIF_GTC |


When time is updated to "2019-11-30T00:00:10Z"
# create party1 stop order, no trade resulting, expires in 10 secs
When the parties place the following orders:
| party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | fb price trigger | reference | ra expires in | ra expiry strategy | fb expires in | fb expiry strategy | error |
| party1 | ETH/DEC19 | buy | 10 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | 25 | stop1 | 10 | EXPIRY_STRATEGY_SUBMIT | 10 | EXPIRY_STRATEGY_SUBMIT | stop order OCOs must not have the same expiry time |
74 changes: 51 additions & 23 deletions core/integration/steps/parties_place_the_following_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,29 @@ func buildStopOrder(

sub := &types.StopOrdersSubmission{}

var (
fbStrategy *types.StopOrderExpiryStrategy
fbStopOrderExpiry *time.Time
raStrategy *types.StopOrderExpiryStrategy
raStopOrderExpiry *time.Time
)
if stopOrderExp := row.StopOrderFBExpirationDate(now); stopOrderExp != 0 {
fbStrategy = ptr.From(row.ExpiryStrategyFB())
fbStopOrderExpiry = ptr.From(time.Unix(0, stopOrderExp))
}
if stopOrderExp := row.StopOrderRAExpirationDate(now); stopOrderExp != 0 {
raStrategy = ptr.From(row.ExpiryStrategyRA())
raStopOrderExpiry = ptr.From(time.Unix(0, stopOrderExp))
}

switch {
case fbPriced != nil:
sub.FallsBelow = &types.StopOrderSetup{
OrderSubmission: submission,
Expiry: &types.StopOrderExpiry{},
Expiry: &types.StopOrderExpiry{
ExpiresAt: fbStopOrderExpiry,
ExpiryStrategy: fbStrategy,
},
Trigger: types.NewPriceStopOrderTrigger(
types.StopOrderTriggerDirectionFallsBelow,
fbPriced.Clone(),
Expand All @@ -343,30 +361,24 @@ func buildStopOrder(
case !fbTrailing.IsZero():
sub.FallsBelow = &types.StopOrderSetup{
OrderSubmission: submission,
Expiry: &types.StopOrderExpiry{},
Expiry: &types.StopOrderExpiry{
ExpiresAt: fbStopOrderExpiry,
ExpiryStrategy: fbStrategy,
},
Trigger: types.NewTrailingStopOrderTrigger(
types.StopOrderTriggerDirectionFallsBelow,
fbTrailing,
),
}
}

var (
strategy *types.StopOrderExpiryStrategy
stopOrderExpiry *time.Time
)
if stopOrderExp := row.StopOrderExpirationDate(now); stopOrderExp != 0 {
strategy = ptr.From(row.ExpiryStrategy())
stopOrderExpiry = ptr.From(time.Unix(0, stopOrderExp))
}

switch {
case raPriced != nil:
sub.RisesAbove = &types.StopOrderSetup{
OrderSubmission: ptr.From(*submission),
Expiry: &types.StopOrderExpiry{
ExpiryStrategy: strategy,
ExpiresAt: stopOrderExpiry,
ExpiryStrategy: raStrategy,
ExpiresAt: raStopOrderExpiry,
},
Trigger: types.NewPriceStopOrderTrigger(
types.StopOrderTriggerDirectionRisesAbove,
Expand All @@ -377,8 +389,8 @@ func buildStopOrder(
sub.RisesAbove = &types.StopOrderSetup{
OrderSubmission: ptr.From(*submission),
Expiry: &types.StopOrderExpiry{
ExpiryStrategy: strategy,
ExpiresAt: stopOrderExpiry,
ExpiryStrategy: raStrategy,
ExpiresAt: raStopOrderExpiry,
},
Trigger: types.NewTrailingStopOrderTrigger(
types.StopOrderTriggerDirectionRisesAbove,
Expand Down Expand Up @@ -437,8 +449,10 @@ func parseSubmitOrderTable(table *godog.Table) []RowWrapper {
"fb trailing",
"ra price trigger",
"ra trailing",
"so expires in",
"so expiry strategy",
"ra expires in",
"ra expiry strategy",
"fb expires in",
"fb expiry strategy",
"pegged reference",
"pegged offset",
"ra size override setting",
Expand Down Expand Up @@ -580,18 +594,32 @@ func (r submitOrderRow) RisesAboveTrailing() num.Decimal {
return r.row.MustDecimal("ra trailing")
}

func (r submitOrderRow) StopOrderExpirationDate(now time.Time) int64 {
if !r.row.HasColumn("so expires in") {
func (r submitOrderRow) StopOrderRAExpirationDate(now time.Time) int64 {
if !r.row.HasColumn("ra expires in") {
return 0
}
return now.Add(r.row.MustDurationSec2("ra expires in")).Local().UnixNano()
}

func (r submitOrderRow) StopOrderFBExpirationDate(now time.Time) int64 {
if !r.row.HasColumn("fb expires in") {
return 0
}
return now.Add(r.row.MustDurationSec2("so expires in")).Local().UnixNano()
return now.Add(r.row.MustDurationSec2("fb expires in")).Local().UnixNano()
}

func (r submitOrderRow) ExpiryStrategyRA() types.StopOrderExpiryStrategy {
if !r.row.HasColumn("ra expiry strategy") {
return types.StopOrderExpiryStrategyCancels
}
return r.row.MustExpiryStrategy("ra expiry strategy")
}

func (r submitOrderRow) ExpiryStrategy() types.StopOrderExpiryStrategy {
if !r.row.HasColumn("so expiry strategy") {
func (r submitOrderRow) ExpiryStrategyFB() types.StopOrderExpiryStrategy {
if !r.row.HasColumn("fb expiry strategy") {
return types.StopOrderExpiryStrategyCancels
}
return r.row.MustExpiryStrategy("so expiry strategy")
return r.row.MustExpiryStrategy("fb expiry strategy")
}

func (r submitOrderRow) PeggedReference() types.PeggedReference {
Expand Down
1 change: 1 addition & 0 deletions core/types/stop_orders.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ const (
StopOrderRejectionNotClosingThePosition StopOrderRejectionReason = vega.StopOrder_REJECTION_REASON_STOP_ORDER_NOT_CLOSING_THE_POSITION
StopOrderRejectionLinkedPercentageInvalid StopOrderRejectionReason = vega.StopOrder_REJECTION_REASON_STOP_ORDER_LINKED_PERCENTAGE_INVALID
StopOrderRejectionNotAllowedDuringOpeningAuction StopOrderRejectionReason = vega.StopOrder_REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION
StopOrderRejectionOCONotAllowedSameExpiryTime StopOrderRejectionReason = vega.StopOrder_REJECTION_REASON_STOP_ORDER_CANNOT_MATCH_OCO_EXPIRY_TIMES
)

type StopOrderExpiry struct {
Expand Down
1 change: 1 addition & 0 deletions datanode/entities/enums.go
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,7 @@ const (
StopOrderRejectionReasonNotAllowedWithoutAPosition = StopOrderRejectionReason(vega.StopOrder_REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_WITHOUT_A_POSITION)
StopOrderRejectionReasonNotClosingThePosition = StopOrderRejectionReason(vega.StopOrder_REJECTION_REASON_STOP_ORDER_NOT_CLOSING_THE_POSITION)
StopOrderRejectionReasonNotAllowedDuringAuction = StopOrderRejectionReason(vega.StopOrder_REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION)
StopOrderRejectionReasonOCONotAllowedSameExpiryTime = StopOrderRejectionReason(vega.StopOrder_REJECTION_REASON_STOP_ORDER_CANNOT_MATCH_OCO_EXPIRY_TIMES)
)

func (s StopOrderRejectionReason) EncodeText(_ *pgtype.ConnInfo, buf []byte) ([]byte, error) {
Expand Down
2 changes: 2 additions & 0 deletions datanode/gateway/graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -3180,6 +3180,8 @@ enum StopOrderRejectionReason {
REJECTION_REASON_STOP_ORDER_NOT_CLOSING_THE_POSITION
"Stop orders are not allowed during the opening auction"
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION
"Stop order cannot have matching OCO expiry times"
REJECTION_REASON_STOP_ORDER_CANNOT_MATCH_OCO_EXPIRY_TIMES
}

"Stop order size override settings"
Expand Down
12 changes: 6 additions & 6 deletions datanode/networkhistory/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,12 +379,12 @@ func TestMain(t *testing.M) {
log.Infof("%s", goldenSourceHistorySegment[4000].HistorySegmentID)
log.Infof("%s", goldenSourceHistorySegment[5000].HistorySegmentID)

panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[1000].HistorySegmentID, "QmdFHth36HfjDnfTfutqjz4MdY1pMKGQg5Yn7FJYjKDKjW", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2000].HistorySegmentID, "QmXBBCQ8ZqHYBQm5sKcGY7MYp3uEXoMPXSkap2tyqNLsYp", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2500].HistorySegmentID, "QmSyn4SxARtdeze6qWDJtoPQebyvnbHPL7bh9qBfXbSDyq", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[3000].HistorySegmentID, "QmYZMW3Epw2UVJejMSb6WNWwjG6vS67A69Dk9iqBkB6eya", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[4000].HistorySegmentID, "QmQJe8VvnCVtFrgVM41FUxe5nRSC4i8eGD7e1WUsw7ca2Z", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[5000].HistorySegmentID, "QmNSZbL1LZddAHekBZvpJwaBvrLmu3zkGVeWfyZrHqE5kZ", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[1000].HistorySegmentID, "QmTUpPCk1gEaWnPo2e1MnEyRhGJGM9mbtjQUt6pfRdUkbJ", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2000].HistorySegmentID, "QmXh3vcyLLhFsZHq958mL1wKirWjMFuLTqiGgpbVQovKp8", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[2500].HistorySegmentID, "QmdLSKg1U3eKNEzFew128tdDtPma3LzoGWmvEPkbv4PnG2", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[3000].HistorySegmentID, "QmbPfH7cbeCf2Wvn9e3L3jQtzE7utmuSJs7PVttd9H86Cn", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[4000].HistorySegmentID, "QmQaGP4z2DXzJ8DVWk8fn2THG4wM9aR3arnbw5tiWsWsk5", snapshots)
panicIfHistorySegmentIdsNotEqual(goldenSourceHistorySegment[5000].HistorySegmentID, "Qmb3gESitQKeNZ2iVbqpFjJHNAEVEDgAG1LscYhvJ8f8jD", snapshots)
}, postgresRuntimePath, sqlFs)

if exitCode != 0 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- +goose Up

ALTER TYPE stop_order_rejection_reason ADD VALUE IF NOT EXISTS 'REJECTION_REASON_STOP_ORDER_CANNOT_MATCH_OCO_EXPIRY_TIMES';

-- +goose Down

-- Do nothing, if it already exists it won't matter and won't be recreated by the up migration.
2 changes: 2 additions & 0 deletions protos/sources/vega/vega.proto
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ message StopOrder {
REJECTION_REASON_STOP_ORDER_LINKED_PERCENTAGE_INVALID = 7;
// Stop orders are not allowed during the opening auction
REJECTION_REASON_STOP_ORDER_NOT_ALLOWED_DURING_OPENING_AUCTION = 8;
// Stop OCO orders cannot have the same expiry timestamp
REJECTION_REASON_STOP_ORDER_CANNOT_MATCH_OCO_EXPIRY_TIMES = 9;
}

// ID of this stop order
Expand Down
Loading

0 comments on commit 5a67c99

Please sign in to comment.