code.vegaprotocol.io/vega@v0.79.0/core/execution/common/mat_intermediate_scores_internal_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 common 17 18 import ( 19 "context" 20 "testing" 21 "time" 22 23 bmocks "code.vegaprotocol.io/vega/core/broker/mocks" 24 "code.vegaprotocol.io/vega/core/events" 25 "code.vegaprotocol.io/vega/core/execution/common/mocks" 26 "code.vegaprotocol.io/vega/core/types" 27 "code.vegaprotocol.io/vega/libs/num" 28 "code.vegaprotocol.io/vega/logging" 29 vgproto "code.vegaprotocol.io/vega/protos/vega" 30 31 "github.com/golang/mock/gomock" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestPublishGameMetricAverageNotional(t *testing.T) { 36 ctx := context.Background() 37 epochService := &DummyEpochEngine{} 38 ctrl := gomock.NewController(t) 39 teams := mocks.NewMockTeams(ctrl) 40 balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) 41 broker := bmocks.NewMockBroker(ctrl) 42 gameScoreEvents := []events.Event{} 43 44 broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 45 broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { 46 if evt.StreamMessage().GetGameScores() != nil { 47 gameScoreEvents = append(gameScoreEvents, evt) 48 } 49 }).AnyTimes() 50 tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) 51 epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) 52 tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) 53 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) 54 55 // add some markets for 2 different assets 56 tracker.MarketProposed("a1", "m1", "z1") 57 tracker.MarketProposed("a1", "m2", "z2") 58 tracker.MarketProposed("a1", "m3", "z3") 59 60 // add some markets for 2 different assets 61 tracker.MarketProposed("a1", "m1", "z1") 62 tracker.MarketProposed("a1", "m2", "z2") 63 tracker.MarketProposed("a1", "m3", "z3") 64 65 // record some values for all metrics 66 tracker.RecordPosition("a1", "p1", "m1", 10, num.NewUint(1), num.DecimalOne(), time.Unix(5, 0)) 67 tracker.RecordPosition("a1", "p1", "m2", 20, num.NewUint(2), num.DecimalOne(), time.Unix(20, 0)) 68 tracker.RecordPosition("a1", "p1", "m3", 30, num.NewUint(3), num.DecimalOne(), time.Unix(30, 0)) 69 tracker.RecordPosition("a1", "p2", "m1", 100, num.NewUint(10), num.DecimalOne(), time.Unix(15, 0)) 70 tracker.RecordPosition("a1", "p2", "m2", 200, num.NewUint(20), num.DecimalOne(), time.Unix(25, 0)) 71 tracker.RecordPosition("a1", "p2", "m3", 300, num.NewUint(30), num.DecimalOne(), time.Unix(45, 0)) 72 73 // end epoch1 74 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: time.Unix(0, 0), EndTime: time.Unix(60, 0)}) 75 76 // get metrics for market m1 with window size=1 77 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).AnyTimes() 78 79 ds1 := &vgproto.DispatchStrategy{ 80 AssetForMetric: "a1", 81 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 82 Markets: []string{"m1"}, 83 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 84 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 85 WindowLength: 1, 86 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 87 } 88 ds2 := &vgproto.DispatchStrategy{ 89 AssetForMetric: "a1", 90 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 91 Markets: []string{"m2"}, 92 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 93 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 94 WindowLength: 1, 95 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 96 } 97 ds3 := &vgproto.DispatchStrategy{ 98 AssetForMetric: "a1", 99 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 100 Markets: []string{"m3"}, 101 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 102 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 103 WindowLength: 1, 104 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 105 } 106 ds4 := &vgproto.DispatchStrategy{ 107 AssetForMetric: "a1", 108 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 109 Markets: []string{"m1", "m2"}, 110 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 111 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 112 WindowLength: 1, 113 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 114 } 115 ds5 := &vgproto.DispatchStrategy{ 116 AssetForMetric: "a1", 117 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 118 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 119 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 120 WindowLength: 1, 121 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 122 } 123 124 // calculate intermediate scores 125 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(60, 0)) 126 require.Equal(t, 5, len(gameScoreEvents)) 127 ps1 := gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 128 require.Equal(t, "p1", ps1[0].Party) 129 require.Equal(t, "p2", ps1[1].Party) 130 require.Equal(t, "0.0000009", ps1[0].Score) 131 require.Equal(t, "0.000075", ps1[1].Score) 132 133 ps2 := gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 134 require.Equal(t, "p1", ps2[0].Party) 135 require.Equal(t, "p2", ps2[1].Party) 136 require.Equal(t, "0.0000026", ps2[0].Score) 137 require.Equal(t, "0.0002333", ps2[1].Score) 138 139 ps3 := gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 140 require.Equal(t, "p1", ps3[0].Party) 141 require.Equal(t, "p2", ps3[1].Party) 142 require.Equal(t, "0.0000045", ps3[0].Score) 143 require.Equal(t, "0.000225", ps3[1].Score) 144 145 ps4 := gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 146 require.Equal(t, "p1", ps4[0].Party) 147 require.Equal(t, "p2", ps4[1].Party) 148 require.Equal(t, "0.0000035", ps4[0].Score) 149 require.Equal(t, "0.0003083", ps4[1].Score) 150 151 ps5 := gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 152 require.Equal(t, "p1", ps5[0].Party) 153 require.Equal(t, "p2", ps5[1].Party) 154 require.Equal(t, "0.000008", ps5[0].Score) 155 require.Equal(t, "0.0005333", ps5[1].Score) 156 157 // now we end the epoch and make sure that we get the exact same results 158 gameScoreEvents = []events.Event{} 159 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: time.Unix(0, 0), EndTime: time.Unix(60, 0)}) 160 161 // we expect that if we take a snapshot of scores now, it looks identical because we didn't change the time 162 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(60, 0)) 163 require.Equal(t, 5, len(gameScoreEvents)) 164 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 165 require.Equal(t, "p1", ps1[0].Party) 166 require.Equal(t, "p2", ps1[1].Party) 167 require.Equal(t, "0.0000009", ps1[0].Score) 168 require.Equal(t, "0.000075", ps1[1].Score) 169 170 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 171 require.Equal(t, "p1", ps2[0].Party) 172 require.Equal(t, "p2", ps2[1].Party) 173 require.Equal(t, "0.0000026", ps2[0].Score) 174 require.Equal(t, "0.0002333", ps2[1].Score) 175 176 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 177 require.Equal(t, "p1", ps3[0].Party) 178 require.Equal(t, "p2", ps3[1].Party) 179 require.Equal(t, "0.0000045", ps3[0].Score) 180 require.Equal(t, "0.000225", ps3[1].Score) 181 182 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 183 require.Equal(t, "p1", ps4[0].Party) 184 require.Equal(t, "p2", ps4[1].Party) 185 require.Equal(t, "0.0000035", ps4[0].Score) 186 require.Equal(t, "0.0003083", ps4[1].Score) 187 188 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 189 require.Equal(t, "p1", ps5[0].Party) 190 require.Equal(t, "p2", ps5[1].Party) 191 require.Equal(t, "0.000008", ps5[0].Score) 192 require.Equal(t, "0.0005333", ps5[1].Score) 193 194 // start epoch 2 and record some activity 195 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(60, 0)}) 196 tracker.RecordPosition("a1", "p1", "m1", 20, num.NewUint(5), num.DecimalOne(), time.Unix(90, 0)) 197 tracker.RecordPosition("a1", "p2", "m2", 10, num.NewUint(10), num.DecimalOne(), time.Unix(75, 0)) 198 tracker.RecordPosition("a2", "p1", "m3", 20, num.NewUint(5), num.DecimalOne(), time.Unix(90, 0)) 199 tracker.RecordPosition("a2", "p2", "m2", 10, num.NewUint(10), num.DecimalOne(), time.Unix(75, 0)) 200 201 gameScoreEvents = []events.Event{} 202 203 // lets look at the events when the window size is 1 204 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(120, 0)) 205 require.Equal(t, 5, len(gameScoreEvents)) 206 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 207 require.Equal(t, "p1", ps1[0].Party) 208 require.Equal(t, "p2", ps1[1].Party) 209 require.Equal(t, "0.0000055", ps1[0].Score) 210 require.Equal(t, "0.0001", ps1[1].Score) 211 212 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 213 require.Equal(t, "p1", ps2[0].Party) 214 require.Equal(t, "p2", ps2[1].Party) 215 require.Equal(t, "0.000004", ps2[0].Score) 216 require.Equal(t, "0.0001075", ps2[1].Score) 217 218 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 219 require.Equal(t, "p1", ps3[0].Party) 220 require.Equal(t, "p2", ps3[1].Party) 221 require.Equal(t, "0.000009", ps3[0].Score) 222 require.Equal(t, "0.0009", ps3[1].Score) 223 224 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 225 require.Equal(t, "p1", ps4[0].Party) 226 require.Equal(t, "p2", ps4[1].Party) 227 require.Equal(t, "0.0000095", ps4[0].Score) 228 require.Equal(t, "0.0002075", ps4[1].Score) 229 230 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 231 require.Equal(t, "p1", ps5[0].Party) 232 require.Equal(t, "p2", ps5[1].Party) 233 require.Equal(t, "0.0000185", ps5[0].Score) 234 require.Equal(t, "0.0011075", ps5[1].Score) 235 236 // now lets change the window to 2: 237 ds1.WindowLength = 2 238 ds2.WindowLength = 2 239 ds3.WindowLength = 2 240 ds4.WindowLength = 2 241 ds5.WindowLength = 2 242 243 gameScoreEvents = []events.Event{} 244 245 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(120, 0)) 246 require.Equal(t, 5, len(gameScoreEvents)) 247 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 248 require.Equal(t, "p1", ps1[0].Party) 249 require.Equal(t, "p2", ps1[1].Party) 250 require.Equal(t, "0.0000032", ps1[0].Score) 251 require.Equal(t, "0.0000875", ps1[1].Score) 252 253 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 254 require.Equal(t, "p1", ps2[0].Party) 255 require.Equal(t, "p2", ps2[1].Party) 256 require.Equal(t, "0.0000033", ps2[0].Score) 257 require.Equal(t, "0.0001704", ps2[1].Score) 258 259 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 260 require.Equal(t, "p1", ps3[0].Party) 261 require.Equal(t, "p2", ps3[1].Party) 262 require.Equal(t, "0.00000675", ps3[0].Score) 263 require.Equal(t, "0.0005625", ps3[1].Score) 264 265 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 266 require.Equal(t, "p1", ps4[0].Party) 267 require.Equal(t, "p2", ps4[1].Party) 268 require.Equal(t, "0.0000065", ps4[0].Score) 269 require.Equal(t, "0.0002579", ps4[1].Score) 270 271 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 272 require.Equal(t, "p1", ps5[0].Party) 273 require.Equal(t, "p2", ps5[1].Party) 274 require.Equal(t, "0.00001325", ps5[0].Score) 275 require.Equal(t, "0.0008204", ps5[1].Score) 276 } 277 278 func TestPublishGameMetricReturnVolatility(t *testing.T) { 279 ctx := context.Background() 280 epochService := &DummyEpochEngine{} 281 ctrl := gomock.NewController(t) 282 teams := mocks.NewMockTeams(ctrl) 283 balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) 284 broker := bmocks.NewMockBroker(ctrl) 285 gameScoreEvents := []events.Event{} 286 broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 287 broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { 288 if evt.StreamMessage().GetGameScores() != nil { 289 gameScoreEvents = append(gameScoreEvents, evt) 290 } 291 }).AnyTimes() 292 tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) 293 epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) 294 tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) 295 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) 296 297 // add some markets for 2 different assets 298 tracker.MarketProposed("a1", "m1", "z1") 299 tracker.MarketProposed("a1", "m2", "z2") 300 tracker.MarketProposed("a1", "m3", "z3") 301 302 // add some markets for 2 different assets 303 tracker.MarketProposed("a1", "m1", "z1") 304 tracker.MarketProposed("a1", "m2", "z2") 305 tracker.MarketProposed("a1", "m3", "z3") 306 307 // record some values for all metrics 308 tracker.RecordPosition("a1", "p1", "m1", 10, num.NewUint(1), num.DecimalOne(), time.Unix(5, 0)) 309 tracker.RecordPosition("a1", "p1", "m2", 20, num.NewUint(2), num.DecimalOne(), time.Unix(20, 0)) 310 tracker.RecordPosition("a1", "p1", "m3", 30, num.NewUint(3), num.DecimalOne(), time.Unix(30, 0)) 311 tracker.RecordPosition("a1", "p2", "m1", 100, num.NewUint(10), num.DecimalOne(), time.Unix(15, 0)) 312 tracker.RecordPosition("a1", "p2", "m2", 200, num.NewUint(20), num.DecimalOne(), time.Unix(25, 0)) 313 tracker.RecordPosition("a1", "p2", "m3", 300, num.NewUint(30), num.DecimalOne(), time.Unix(45, 0)) 314 tracker.RecordPosition("a1", "p3", "m1", 10, num.NewUint(1), num.DecimalOne(), time.Unix(10, 0)) 315 tracker.RecordPosition("a1", "p3", "m2", 20, num.NewUint(2), num.DecimalOne(), time.Unix(10, 0)) 316 tracker.RecordPosition("a1", "p3", "m3", 30, num.NewUint(3), num.DecimalOne(), time.Unix(10, 0)) 317 318 tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(80)) 319 tracker.RecordM2M("a1", "p2", "m1", num.DecimalFromInt64(20)) 320 tracker.RecordM2M("a1", "p3", "m1", num.DecimalFromInt64(-100)) 321 tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(10)) 322 tracker.RecordM2M("a1", "p2", "m1", num.DecimalFromInt64(-10)) 323 tracker.RecordM2M("a1", "p1", "m2", num.DecimalFromInt64(50)) 324 tracker.RecordM2M("a1", "p2", "m2", num.DecimalFromInt64(-5)) 325 tracker.RecordM2M("a1", "p3", "m2", num.DecimalFromInt64(-45)) 326 tracker.RecordM2M("a1", "p1", "m3", num.DecimalFromInt64(-35)) 327 tracker.RecordM2M("a1", "p2", "m3", num.DecimalFromInt64(35)) 328 329 // get metrics for market m1 with window size=1 330 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).AnyTimes() 331 332 ds1 := &vgproto.DispatchStrategy{ 333 AssetForMetric: "a1", 334 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, 335 Markets: []string{"m1"}, 336 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 337 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 338 WindowLength: 1, 339 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 340 } 341 ds2 := &vgproto.DispatchStrategy{ 342 AssetForMetric: "a1", 343 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, 344 Markets: []string{"m2"}, 345 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 346 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 347 WindowLength: 1, 348 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 349 } 350 ds3 := &vgproto.DispatchStrategy{ 351 AssetForMetric: "a1", 352 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, 353 Markets: []string{"m3"}, 354 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 355 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 356 WindowLength: 1, 357 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 358 } 359 ds4 := &vgproto.DispatchStrategy{ 360 AssetForMetric: "a1", 361 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, 362 Markets: []string{"m1", "m2"}, 363 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 364 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 365 WindowLength: 1, 366 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 367 } 368 ds5 := &vgproto.DispatchStrategy{ 369 AssetForMetric: "a1", 370 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, 371 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 372 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 373 WindowLength: 1, 374 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 375 } 376 377 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(60, 0)) 378 require.Equal(t, 5, len(gameScoreEvents)) 379 ps1 := gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 380 require.Equal(t, "p1", ps1[0].Party) 381 require.Equal(t, "p2", ps1[1].Party) 382 require.Equal(t, "0", ps1[0].Score) 383 require.Equal(t, "0", ps1[1].Score) 384 385 ps2 := gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 386 require.Equal(t, "p1", ps2[0].Party) 387 require.Equal(t, "p2", ps2[1].Party) 388 require.Equal(t, "0", ps2[0].Score) 389 require.Equal(t, "0", ps2[1].Score) 390 391 ps3 := gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 392 require.Equal(t, "p1", ps3[0].Party) 393 require.Equal(t, "p2", ps3[1].Party) 394 require.Equal(t, "0", ps3[0].Score) 395 require.Equal(t, "0", ps3[1].Score) 396 397 ps4 := gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 398 require.Equal(t, "p1", ps4[0].Party) 399 require.Equal(t, "p2", ps4[1].Party) 400 require.Equal(t, "0", ps4[0].Score) 401 require.Equal(t, "0", ps4[1].Score) 402 403 ps5 := gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 404 require.Equal(t, "p1", ps5[0].Party) 405 require.Equal(t, "p2", ps5[1].Party) 406 require.Equal(t, "0", ps5[0].Score) 407 require.Equal(t, "0", ps5[1].Score) 408 409 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: time.Unix(0, 0), EndTime: time.Unix(60, 0)}) 410 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(60, 0)}) 411 tracker.RecordPosition("a1", "p1", "m1", 20, num.NewUint(5), num.DecimalOne(), time.Unix(90, 0)) 412 tracker.RecordPosition("a1", "p2", "m2", 10, num.NewUint(10), num.DecimalOne(), time.Unix(75, 0)) 413 tracker.RecordPosition("a1", "p3", "m1", 10, num.NewUint(10), num.DecimalOne(), time.Unix(75, 0)) 414 tracker.RecordPosition("a2", "p1", "m3", 20, num.NewUint(5), num.DecimalOne(), time.Unix(90, 0)) 415 tracker.RecordPosition("a2", "p2", "m2", 10, num.NewUint(10), num.DecimalOne(), time.Unix(75, 0)) 416 417 tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(45)) 418 tracker.RecordM2M("a1", "p3", "m1", num.DecimalFromInt64(-45)) 419 tracker.RecordM2M("a1", "p1", "m2", num.DecimalFromInt64(-10)) 420 tracker.RecordM2M("a1", "p2", "m2", num.DecimalFromInt64(10)) 421 // nothing in m3 422 423 ds1.WindowLength = 2 424 ds2.WindowLength = 2 425 ds3.WindowLength = 2 426 ds4.WindowLength = 2 427 ds5.WindowLength = 2 428 429 gameScoreEvents = []events.Event{} 430 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(120, 0)) 431 require.Equal(t, 5, len(gameScoreEvents)) 432 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 433 require.Equal(t, "p1", ps1[0].Party) 434 require.Equal(t, "p2", ps1[1].Party) 435 require.Equal(t, "0.086044426422046", ps1[0].Score) 436 require.Equal(t, "0", ps1[1].Score) 437 require.Equal(t, true, ps1[0].IsEligible) 438 require.Equal(t, false, ps1[1].IsEligible) 439 440 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 441 require.Equal(t, "p1", ps2[0].Party) 442 require.Equal(t, "p2", ps2[1].Party) 443 require.Equal(t, "0.2214532481172412", ps2[0].Score) 444 require.Equal(t, "85.1257359604949139", ps2[1].Score) 445 require.Equal(t, true, ps2[0].IsEligible) 446 require.Equal(t, true, ps2[1].IsEligible) 447 448 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 449 require.Equal(t, "p1", ps3[0].Party) 450 require.Equal(t, "p2", ps3[1].Party) 451 require.Equal(t, "0", ps3[0].Score) 452 require.Equal(t, "0", ps3[1].Score) 453 require.Equal(t, false, ps3[0].IsEligible) 454 require.Equal(t, false, ps1[1].IsEligible) 455 456 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 457 require.Equal(t, "p1", ps4[0].Party) 458 require.Equal(t, "p2", ps4[1].Party) 459 require.Equal(t, "0.0326518156928779", ps4[0].Score) 460 require.Equal(t, "574.5715725244936759", ps4[1].Score) 461 require.Equal(t, true, ps4[0].IsEligible) 462 require.Equal(t, true, ps4[1].IsEligible) 463 464 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 465 require.Equal(t, "p1", ps5[0].Party) 466 require.Equal(t, "p2", ps5[1].Party) 467 require.Equal(t, "0.0524262906455334", ps5[0].Score) 468 require.Equal(t, "27.2358805547724978", ps5[1].Score) 469 require.Equal(t, true, ps5[0].IsEligible) 470 require.Equal(t, true, ps5[1].IsEligible) 471 472 // now end the epoch properly 473 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: time.Unix(60, 0), EndTime: time.Unix(120, 0)}) 474 epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(120, 0)}) 475 476 // record some m2ms 477 tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(20)) 478 tracker.RecordM2M("a1", "p3", "m1", num.DecimalFromInt64(-25)) 479 tracker.RecordM2M("a1", "p1", "m2", num.DecimalFromInt64(-15)) 480 tracker.RecordM2M("a1", "p2", "m2", num.DecimalFromInt64(15)) 481 482 gameScoreEvents = []events.Event{} 483 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(150, 0)) 484 require.Equal(t, 5, len(gameScoreEvents)) 485 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 486 require.Equal(t, "p1", ps1[0].Party) 487 require.Equal(t, "p2", ps1[1].Party) 488 require.Equal(t, "1", ps1[0].Score) 489 require.Equal(t, "0", ps1[1].Score) 490 require.Equal(t, true, ps1[0].IsEligible) 491 require.Equal(t, false, ps1[1].IsEligible) 492 493 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 494 require.Equal(t, "p1", ps2[0].Party) 495 require.Equal(t, "p2", ps2[1].Party) 496 require.Equal(t, "64", ps2[0].Score) 497 require.Equal(t, "2.2746573501746843", ps2[1].Score) 498 require.Equal(t, true, ps2[0].IsEligible) 499 require.Equal(t, true, ps2[1].IsEligible) 500 501 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 502 require.Equal(t, "p1", ps3[0].Party) 503 require.Equal(t, "p2", ps3[1].Party) 504 require.Equal(t, "0", ps3[0].Score) 505 require.Equal(t, "0", ps3[1].Score) 506 require.Equal(t, false, ps3[0].IsEligible) 507 require.Equal(t, false, ps1[1].IsEligible) 508 509 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 510 require.Equal(t, "p1", ps4[0].Party) 511 require.Equal(t, "p2", ps4[1].Party) 512 require.Equal(t, "0.7901234567901235", ps4[0].Score) 513 require.Equal(t, "2.2746573501746843", ps4[1].Score) 514 require.Equal(t, true, ps4[0].IsEligible) 515 require.Equal(t, true, ps4[1].IsEligible) 516 517 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 518 require.Equal(t, "p1", ps5[0].Party) 519 require.Equal(t, "p2", ps5[1].Party) 520 require.Equal(t, "0.7901234567901235", ps5[0].Score) 521 require.Equal(t, "2.2746573501746843", ps5[1].Score) 522 require.Equal(t, true, ps5[0].IsEligible) 523 require.Equal(t, true, ps5[1].IsEligible) 524 } 525 526 func TestPublishGameMetricRelativeReturn(t *testing.T) { 527 ctx := context.Background() 528 epochService := &DummyEpochEngine{} 529 ctrl := gomock.NewController(t) 530 teams := mocks.NewMockTeams(ctrl) 531 balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) 532 broker := bmocks.NewMockBroker(ctrl) 533 gameScoreEvents := []events.Event{} 534 broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 535 broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { 536 if evt.StreamMessage().GetGameScores() != nil { 537 gameScoreEvents = append(gameScoreEvents, evt) 538 } 539 }).AnyTimes() 540 tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) 541 epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) 542 tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) 543 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) 544 545 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).AnyTimes() 546 547 ds1 := &vgproto.DispatchStrategy{ 548 AssetForMetric: "a1", 549 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, 550 Markets: []string{"m1"}, 551 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 552 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 553 WindowLength: 1, 554 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 555 } 556 ds2 := &vgproto.DispatchStrategy{ 557 AssetForMetric: "a1", 558 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, 559 Markets: []string{"m2"}, 560 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 561 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 562 WindowLength: 1, 563 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 564 } 565 ds3 := &vgproto.DispatchStrategy{ 566 AssetForMetric: "a1", 567 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, 568 Markets: []string{"m3"}, 569 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 570 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 571 WindowLength: 1, 572 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 573 } 574 ds4 := &vgproto.DispatchStrategy{ 575 AssetForMetric: "a1", 576 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, 577 Markets: []string{"m1", "m2"}, 578 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 579 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 580 WindowLength: 1, 581 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 582 } 583 ds5 := &vgproto.DispatchStrategy{ 584 AssetForMetric: "a1", 585 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, 586 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 587 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 588 WindowLength: 1, 589 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 590 } 591 592 // add some markets for 2 different assets 593 tracker.MarketProposed("a1", "m1", "z1") 594 tracker.MarketProposed("a1", "m2", "z2") 595 tracker.MarketProposed("a1", "m3", "z3") 596 597 // record some values for all metrics 598 tracker.RecordPosition("a1", "p1", "m1", 10, num.NewUint(1), num.DecimalOne(), time.Unix(5, 0)) 599 tracker.RecordPosition("a1", "p1", "m2", 20, num.NewUint(2), num.DecimalOne(), time.Unix(20, 0)) 600 tracker.RecordPosition("a1", "p1", "m3", 30, num.NewUint(3), num.DecimalOne(), time.Unix(30, 0)) 601 tracker.RecordPosition("a1", "p2", "m1", 100, num.NewUint(10), num.DecimalOne(), time.Unix(15, 0)) 602 tracker.RecordPosition("a1", "p2", "m2", 200, num.NewUint(20), num.DecimalOne(), time.Unix(25, 0)) 603 tracker.RecordPosition("a1", "p2", "m3", 300, num.NewUint(30), num.DecimalOne(), time.Unix(45, 0)) 604 605 tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(-100)) 606 tracker.RecordM2M("a1", "p2", "m1", num.DecimalFromInt64(100)) 607 tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(250)) 608 tracker.RecordM2M("a1", "p2", "m1", num.DecimalFromInt64(-250)) 609 tracker.RecordM2M("a1", "p1", "m2", num.DecimalFromInt64(-50)) 610 tracker.RecordM2M("a1", "p2", "m2", num.DecimalFromInt64(50)) 611 tracker.RecordM2M("a1", "p1", "m3", num.DecimalFromInt64(100)) 612 tracker.RecordM2M("a1", "p2", "m3", num.DecimalFromInt64(-100)) 613 614 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(60, 0)) 615 require.Equal(t, 5, len(gameScoreEvents)) 616 ps1 := gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 617 require.Equal(t, "p1", ps1[0].Party) 618 require.Equal(t, "p2", ps1[1].Party) 619 require.Equal(t, "16.3636375537190948", ps1[0].Score) 620 require.Equal(t, "-2", ps1[1].Score) 621 622 ps2 := gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 623 require.Equal(t, "p1", ps2[0].Party) 624 require.Equal(t, "p2", ps2[1].Party) 625 require.Equal(t, "-3.7500003750000375", ps2[0].Score) 626 require.Equal(t, "0.4285714530612259", ps2[1].Score) 627 628 ps3 := gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 629 require.Equal(t, "p1", ps3[0].Party) 630 require.Equal(t, "p2", ps3[1].Party) 631 require.Equal(t, "6.6666666666666667", ps3[0].Score) 632 require.Equal(t, "-1.3333333333333333", ps3[1].Score) 633 634 ps4 := gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 635 require.Equal(t, "p1", ps4[0].Party) 636 require.Equal(t, "p2", ps4[1].Party) 637 require.Equal(t, "12.6136371787190573", ps4[0].Score) 638 require.Equal(t, "-1.5714285469387741", ps4[1].Score) 639 640 ps5 := gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 641 require.Equal(t, "p1", ps5[0].Party) 642 require.Equal(t, "p2", ps5[1].Party) 643 require.Equal(t, "19.280303845385724", ps5[0].Score) 644 require.Equal(t, "-2.9047618802721074", ps5[1].Score) 645 646 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: time.Unix(0, 0), EndTime: time.Unix(60, 0)}) 647 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(60, 0)}) 648 649 tracker.RecordPosition("a1", "p1", "m1", 20, num.NewUint(5), num.DecimalOne(), time.Unix(90, 0)) 650 tracker.RecordPosition("a1", "p2", "m2", 10, num.NewUint(10), num.DecimalOne(), time.Unix(75, 0)) 651 tracker.RecordPosition("a2", "p1", "m3", 20, num.NewUint(5), num.DecimalOne(), time.Unix(90, 0)) 652 tracker.RecordPosition("a2", "p2", "m2", 10, num.NewUint(10), num.DecimalOne(), time.Unix(75, 0)) 653 654 tracker.RecordM2M("a1", "p1", "m1", num.DecimalFromInt64(450)) 655 tracker.RecordM2M("a1", "p2", "m1", num.DecimalFromInt64(-450)) 656 tracker.RecordM2M("a1", "p1", "m2", num.DecimalFromInt64(-100)) 657 tracker.RecordM2M("a1", "p2", "m2", num.DecimalFromInt64(100)) 658 // nothing in m3 659 660 gameScoreEvents = []events.Event{} 661 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(120, 0)) 662 require.Equal(t, 5, len(gameScoreEvents)) 663 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 664 require.Equal(t, "p1", ps1[0].Party) 665 require.Equal(t, "p2", ps1[1].Party) 666 require.Equal(t, "30", ps1[0].Score) 667 require.Equal(t, "-4.5", ps1[1].Score) 668 669 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 670 require.Equal(t, "p1", ps2[0].Party) 671 require.Equal(t, "p2", ps2[1].Party) 672 require.Equal(t, "-5", ps2[0].Score) 673 require.Equal(t, "1.7391304347826087", ps2[1].Score) 674 675 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 676 require.Equal(t, "p1", ps3[0].Party) 677 require.Equal(t, "p2", ps3[1].Party) 678 require.Equal(t, "0", ps3[0].Score) 679 require.Equal(t, "0", ps3[1].Score) 680 require.False(t, ps3[0].IsEligible) 681 require.False(t, ps3[1].IsEligible) 682 683 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 684 require.Equal(t, "p1", ps4[0].Party) 685 require.Equal(t, "p2", ps4[1].Party) 686 require.Equal(t, "25", ps4[0].Score) 687 require.Equal(t, "-2.7608695652173913", ps4[1].Score) 688 689 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 690 require.Equal(t, "p1", ps5[0].Party) 691 require.Equal(t, "p2", ps5[1].Party) 692 require.Equal(t, "25", ps5[0].Score) 693 require.Equal(t, "-2.7608695652173913", ps5[1].Score) 694 695 // check with window length = 2 696 ds1.WindowLength = 2 697 ds2.WindowLength = 2 698 ds3.WindowLength = 2 699 ds4.WindowLength = 2 700 ds5.WindowLength = 2 701 702 gameScoreEvents = []events.Event{} 703 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5}, time.Unix(120, 0)) 704 require.Equal(t, 5, len(gameScoreEvents)) 705 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 706 require.Equal(t, "p1", ps1[0].Party) 707 require.Equal(t, "p2", ps1[1].Party) 708 require.Equal(t, "23.1818187768595474", ps1[0].Score) 709 require.Equal(t, "-3.25", ps1[1].Score) 710 require.Equal(t, true, ps1[0].IsEligible) 711 require.Equal(t, true, ps1[1].IsEligible) 712 713 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 714 require.Equal(t, "p1", ps2[0].Party) 715 require.Equal(t, "p2", ps2[1].Party) 716 require.Equal(t, "-4.3750001875000188", ps2[0].Score) 717 require.Equal(t, "1.0838509439219173", ps2[1].Score) 718 require.Equal(t, true, ps2[0].IsEligible) 719 require.Equal(t, true, ps2[1].IsEligible) 720 721 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 722 require.Equal(t, "p1", ps3[0].Party) 723 require.Equal(t, "p2", ps3[1].Party) 724 require.Equal(t, "3.3333333333333334", ps3[0].Score) 725 require.Equal(t, "-0.6666666666666667", ps3[1].Score) 726 require.Equal(t, true, ps3[0].IsEligible) 727 require.Equal(t, true, ps1[1].IsEligible) 728 729 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 730 require.Equal(t, "p1", ps4[0].Party) 731 require.Equal(t, "p2", ps4[1].Party) 732 require.Equal(t, "18.8068185893595287", ps4[0].Score) 733 require.Equal(t, "-2.1661490560780827", ps4[1].Score) 734 require.Equal(t, true, ps4[0].IsEligible) 735 require.Equal(t, true, ps4[1].IsEligible) 736 737 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 738 require.Equal(t, "p1", ps5[0].Party) 739 require.Equal(t, "p2", ps5[1].Party) 740 require.Equal(t, "22.140151922692862", ps5[0].Score) 741 require.Equal(t, "-2.8328157227447494", ps5[1].Score) 742 require.Equal(t, true, ps5[0].IsEligible) 743 require.Equal(t, true, ps5[1].IsEligible) 744 } 745 746 func TestPublishGameMetricRealisedReturn(t *testing.T) { 747 ctx := context.Background() 748 epochService := &DummyEpochEngine{} 749 ctrl := gomock.NewController(t) 750 teams := mocks.NewMockTeams(ctrl) 751 balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) 752 broker := bmocks.NewMockBroker(ctrl) 753 gameScoreEvents := []events.Event{} 754 broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 755 broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { 756 if evt.StreamMessage().GetGameScores() != nil { 757 gameScoreEvents = append(gameScoreEvents, evt) 758 } 759 }).AnyTimes() 760 tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) 761 epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) 762 tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) 763 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) 764 765 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).AnyTimes() 766 767 tracker.MarketProposed("a1", "m1", "z1") 768 769 ds1 := &vgproto.DispatchStrategy{ 770 AssetForMetric: "a1", 771 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, 772 Markets: []string{"m1"}, 773 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 774 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 775 WindowLength: 1, 776 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 777 } 778 ds2 := &vgproto.DispatchStrategy{ 779 AssetForMetric: "a1", 780 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, 781 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 782 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 783 WindowLength: 1, 784 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 785 } 786 787 tracker.RecordFundingPayment("a1", "p1", "m1", num.DecimalFromInt64(100)) 788 tracker.RecordRealisedPosition("a1", "p1", "m1", num.DecimalFromInt64(-50)) 789 tracker.RecordFundingPayment("a1", "p1", "m1", num.DecimalFromInt64(-200)) 790 tracker.RecordRealisedPosition("a1", "p1", "m1", num.DecimalFromInt64(20)) 791 tracker.RecordFundingPayment("a1", "p2", "m1", num.DecimalFromInt64(-100)) 792 tracker.RecordRealisedPosition("a1", "p2", "m1", num.DecimalFromInt64(-10)) 793 tracker.RecordRealisedPosition("a1", "p2", "m1", num.DecimalFromInt64(20)) 794 tracker.RecordFundingPayment("a1", "p3", "m1", num.DecimalFromInt64(200)) 795 796 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2}, time.Unix(60, 0)) 797 require.Equal(t, 2, len(gameScoreEvents)) 798 ps1 := gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 799 require.Equal(t, "p1", ps1[0].Party) 800 require.Equal(t, "p2", ps1[1].Party) 801 require.Equal(t, "p3", ps1[2].Party) 802 require.Equal(t, "-130", ps1[0].Score) 803 require.Equal(t, "-90", ps1[1].Score) 804 require.Equal(t, "200", ps1[2].Score) 805 806 ps2 := gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 807 require.Equal(t, "p1", ps2[0].Party) 808 require.Equal(t, "p2", ps2[1].Party) 809 require.Equal(t, "p3", ps2[2].Party) 810 require.Equal(t, "-130", ps2[0].Score) 811 require.Equal(t, "-90", ps2[1].Score) 812 require.Equal(t, "200", ps2[2].Score) 813 814 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: time.Unix(0, 0), EndTime: time.Unix(60, 0)}) 815 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(60, 0)}) 816 817 tracker.RecordFundingPayment("a1", "p1", "m1", num.DecimalFromInt64(-30)) 818 tracker.RecordRealisedPosition("a1", "p2", "m1", num.DecimalFromInt64(70)) 819 tracker.RecordRealisedPosition("a1", "p2", "m1", num.DecimalFromInt64(80)) 820 tracker.RecordRealisedPosition("a1", "p3", "m1", num.DecimalFromInt64(-50)) 821 822 // with window size1 823 gameScoreEvents = []events.Event{} 824 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2}, time.Unix(120, 0)) 825 require.Equal(t, 2, len(gameScoreEvents)) 826 827 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 828 require.Equal(t, "p1", ps1[0].Party) 829 require.Equal(t, "p2", ps1[1].Party) 830 require.Equal(t, "p3", ps1[2].Party) 831 require.Equal(t, "-30", ps1[0].Score) 832 require.Equal(t, "150", ps1[1].Score) 833 require.Equal(t, "-50", ps1[2].Score) 834 835 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 836 require.Equal(t, "p1", ps2[0].Party) 837 require.Equal(t, "p2", ps2[1].Party) 838 require.Equal(t, "p3", ps2[2].Party) 839 require.Equal(t, "-30", ps2[0].Score) 840 require.Equal(t, "150", ps2[1].Score) 841 require.Equal(t, "-50", ps2[2].Score) 842 843 // check with window length = 2 844 ds1.WindowLength = 2 845 ds2.WindowLength = 2 846 847 gameScoreEvents = []events.Event{} 848 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2}, time.Unix(120, 0)) 849 require.Equal(t, 2, len(gameScoreEvents)) 850 851 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 852 require.Equal(t, "p1", ps1[0].Party) 853 require.Equal(t, "p2", ps1[1].Party) 854 require.Equal(t, "p3", ps1[2].Party) 855 require.Equal(t, "-80", ps1[0].Score) 856 require.Equal(t, "30", ps1[1].Score) 857 require.Equal(t, "75", ps1[2].Score) 858 859 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 860 require.Equal(t, "p1", ps2[0].Party) 861 require.Equal(t, "p2", ps2[1].Party) 862 require.Equal(t, "p3", ps2[2].Party) 863 require.Equal(t, "-80", ps2[0].Score) 864 require.Equal(t, "30", ps2[1].Score) 865 require.Equal(t, "75", ps2[2].Score) 866 } 867 868 func TestPublishGameMetricFees(t *testing.T) { 869 ctx := context.Background() 870 epochService := &DummyEpochEngine{} 871 ctrl := gomock.NewController(t) 872 teams := mocks.NewMockTeams(ctrl) 873 balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) 874 broker := bmocks.NewMockBroker(ctrl) 875 gameScoreEvents := []events.Event{} 876 broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() 877 broker.EXPECT().Send(gomock.Any()).Do(func(evt events.Event) { 878 if evt.StreamMessage().GetGameScores() != nil { 879 gameScoreEvents = append(gameScoreEvents, evt) 880 } 881 }).AnyTimes() 882 tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) 883 epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) 884 tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) 885 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) 886 887 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).AnyTimes() 888 889 tracker.MarketProposed("a1", "m1", "me") 890 tracker.MarketProposed("a1", "m2", "me2") 891 892 ds1 := &vgproto.DispatchStrategy{ 893 AssetForMetric: "a1", 894 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, 895 Markets: []string{"m1"}, 896 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 897 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 898 WindowLength: 1, 899 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 900 } 901 ds2 := &vgproto.DispatchStrategy{ 902 AssetForMetric: "a1", 903 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, 904 Markets: []string{"m2"}, 905 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 906 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 907 WindowLength: 1, 908 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 909 } 910 ds3 := &vgproto.DispatchStrategy{ 911 AssetForMetric: "a1", 912 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, 913 Markets: []string{"m1", "m2"}, 914 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 915 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 916 WindowLength: 1, 917 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 918 } 919 ds4 := &vgproto.DispatchStrategy{ 920 AssetForMetric: "a1", 921 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, 922 Markets: []string{}, 923 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 924 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 925 WindowLength: 1, 926 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 927 } 928 ds5 := &vgproto.DispatchStrategy{ 929 AssetForMetric: "a1", 930 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, 931 Markets: []string{"m1"}, 932 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 933 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 934 WindowLength: 1, 935 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 936 } 937 ds6 := &vgproto.DispatchStrategy{ 938 AssetForMetric: "a1", 939 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, 940 Markets: []string{"m2"}, 941 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 942 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 943 WindowLength: 1, 944 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 945 } 946 ds7 := &vgproto.DispatchStrategy{ 947 AssetForMetric: "a1", 948 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, 949 Markets: []string{"m1", "m2"}, 950 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 951 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 952 WindowLength: 1, 953 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 954 } 955 ds8 := &vgproto.DispatchStrategy{ 956 AssetForMetric: "a1", 957 Metric: vgproto.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, 958 Markets: []string{}, 959 EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, 960 IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, 961 WindowLength: 1, 962 DistributionStrategy: vgproto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, 963 } 964 965 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) 966 967 tracker.MarketProposed("a1", "market1", "me") 968 tracker.MarketProposed("a1", "market2", "me2") 969 970 // update with a few transfers 971 transfersM1 := []*types.Transfer{ 972 {Owner: "p1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(100)}}, 973 {Owner: "p1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(200)}}, 974 {Owner: "p1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(400)}}, 975 {Owner: "p1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(300)}}, 976 {Owner: "p2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(900)}}, 977 {Owner: "p2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(800)}}, 978 {Owner: "p2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(600)}}, 979 {Owner: "p2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(200)}}, 980 } 981 tracker.UpdateFeesFromTransfers("a1", "m1", transfersM1) 982 983 transfersM2 := []*types.Transfer{ 984 {Owner: "p1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "a2", Amount: num.NewUint(150)}}, 985 {Owner: "p2", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "a2", Amount: num.NewUint(150)}}, 986 } 987 tracker.UpdateFeesFromTransfers("a1", "m2", transfersM2) 988 989 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5, ds6, ds7, ds8}, time.Unix(10, 0)) 990 require.Equal(t, 8, len(gameScoreEvents)) 991 992 ps1 := gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 993 require.Equal(t, "p1", ps1[0].Party) 994 require.Equal(t, "0.3333333333333333", ps1[0].Score) 995 require.Equal(t, "p2", ps1[1].Party) 996 require.Equal(t, "0.6666666666666667", ps1[1].Score) 997 998 ps2 := gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 999 require.Equal(t, "p1", ps2[0].Party) 1000 require.Equal(t, "0", ps2[0].Score) 1001 require.False(t, ps2[0].IsEligible) 1002 require.Equal(t, "p2", ps2[1].Party) 1003 require.Equal(t, "1", ps2[1].Score) 1004 1005 ps3 := gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 1006 require.Equal(t, "p1", ps3[0].Party) 1007 require.Equal(t, "0.303030303030303", ps3[0].Score) 1008 require.Equal(t, "p2", ps3[1].Party) 1009 require.Equal(t, "0.696969696969697", ps3[1].Score) 1010 1011 ps4 := gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 1012 require.Equal(t, "p1", ps4[0].Party) 1013 require.Equal(t, "0.303030303030303", ps4[0].Score) 1014 require.Equal(t, "p2", ps4[1].Party) 1015 require.Equal(t, "0.696969696969697", ps4[1].Score) 1016 1017 ps5 := gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 1018 require.Equal(t, "p1", ps5[0].Party) 1019 require.Equal(t, "0.25", ps5[0].Score) 1020 require.Equal(t, "p2", ps5[1].Party) 1021 require.Equal(t, "0.75", ps5[1].Score) 1022 1023 ps6 := gameScoreEvents[5].StreamMessage().GetGameScores().PartyScores 1024 require.Equal(t, "p1", ps6[0].Party) 1025 require.Equal(t, "1", ps6[0].Score) 1026 require.Equal(t, "p2", ps6[1].Party) 1027 require.Equal(t, "0", ps6[1].Score) 1028 require.False(t, ps6[1].IsEligible) 1029 1030 ps7 := gameScoreEvents[6].StreamMessage().GetGameScores().PartyScores 1031 require.Equal(t, "p1", ps7[0].Party) 1032 require.Equal(t, "0.3023255813953488", ps7[0].Score) 1033 require.Equal(t, "p2", ps7[1].Party) 1034 require.Equal(t, "0.6976744186046512", ps7[1].Score) 1035 1036 ps8 := gameScoreEvents[7].StreamMessage().GetGameScores().PartyScores 1037 require.Equal(t, "p1", ps8[0].Party) 1038 require.Equal(t, "0.3023255813953488", ps8[0].Score) 1039 require.Equal(t, "p2", ps8[1].Party) 1040 require.Equal(t, "0.6976744186046512", ps8[1].Score) 1041 1042 epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END}) 1043 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START}) 1044 1045 ds1.WindowLength = 2 1046 ds2.WindowLength = 2 1047 ds3.WindowLength = 2 1048 ds4.WindowLength = 2 1049 ds5.WindowLength = 2 1050 ds6.WindowLength = 2 1051 ds7.WindowLength = 2 1052 ds8.WindowLength = 2 1053 1054 // pay/receive some fees in me for the new epoch 1055 transfersM1 = []*types.Transfer{ 1056 {Owner: "p1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(300)}}, 1057 {Owner: "p1", Type: types.TransferTypeMakerFeePay, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(100)}}, 1058 {Owner: "p2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "a1", Amount: num.NewUint(900)}}, 1059 } 1060 tracker.UpdateFeesFromTransfers("a1", "m1", transfersM1) 1061 1062 gameScoreEvents = []events.Event{} 1063 tracker.PublishGameMetric(ctx, []*vgproto.DispatchStrategy{ds1, ds2, ds3, ds4, ds5, ds6, ds7, ds8}, time.Unix(20, 0)) 1064 require.Equal(t, 8, len(gameScoreEvents)) 1065 1066 ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores 1067 require.Equal(t, "p1", ps1[0].Party) 1068 require.Equal(t, "0.375", ps1[0].Score) 1069 require.Equal(t, "p2", ps1[1].Party) 1070 require.Equal(t, "0.625", ps1[1].Score) 1071 1072 ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores 1073 require.Equal(t, "p1", ps2[0].Party) 1074 require.Equal(t, "0", ps2[0].Score) 1075 require.False(t, ps2[0].IsEligible) 1076 require.Equal(t, "p2", ps2[1].Party) 1077 require.Equal(t, "1", ps2[1].Score) 1078 1079 ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores 1080 require.Equal(t, "p1", ps3[0].Party) 1081 require.Equal(t, "0.3428571428571429", ps3[0].Score) 1082 require.Equal(t, "p2", ps3[1].Party) 1083 require.Equal(t, "0.6571428571428571", ps3[1].Score) 1084 1085 ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores 1086 require.Equal(t, "p1", ps4[0].Party) 1087 require.Equal(t, "0.3428571428571429", ps4[0].Score) 1088 require.Equal(t, "p2", ps4[1].Party) 1089 require.Equal(t, "0.6571428571428571", ps4[1].Score) 1090 1091 ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores 1092 require.Equal(t, "p1", ps5[0].Party) 1093 require.Equal(t, "0.25", ps5[0].Score) 1094 require.Equal(t, "p2", ps5[1].Party) 1095 require.Equal(t, "0.75", ps5[1].Score) 1096 1097 ps6 = gameScoreEvents[5].StreamMessage().GetGameScores().PartyScores 1098 require.Equal(t, "p1", ps6[0].Party) 1099 require.Equal(t, "1", ps6[0].Score) 1100 require.Equal(t, "p2", ps6[1].Party) 1101 require.Equal(t, "0", ps6[1].Score) 1102 require.False(t, ps6[1].IsEligible) 1103 1104 ps7 = gameScoreEvents[6].StreamMessage().GetGameScores().PartyScores 1105 require.Equal(t, "p1", ps7[0].Party) 1106 require.Equal(t, "0.2835820895522388", ps7[0].Score) 1107 require.Equal(t, "p2", ps7[1].Party) 1108 require.Equal(t, "0.7164179104477612", ps7[1].Score) 1109 1110 ps8 = gameScoreEvents[7].StreamMessage().GetGameScores().PartyScores 1111 require.Equal(t, "p1", ps8[0].Party) 1112 require.Equal(t, "0.2835820895522388", ps8[0].Score) 1113 require.Equal(t, "p2", ps8[1].Party) 1114 require.Equal(t, "0.7164179104477612", ps8[1].Score) 1115 }