code.vegaprotocol.io/vega@v0.79.0/datanode/gateway/graphql/market_data_resolvers.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 gql 17 18 import ( 19 "context" 20 "errors" 21 "io" 22 "strconv" 23 24 "code.vegaprotocol.io/vega/datanode/vegatime" 25 "code.vegaprotocol.io/vega/logging" 26 v2 "code.vegaprotocol.io/vega/protos/data-node/api/v2" 27 types "code.vegaprotocol.io/vega/protos/vega" 28 vega "code.vegaprotocol.io/vega/protos/vega" 29 vegapb "code.vegaprotocol.io/vega/protos/vega" 30 31 "google.golang.org/grpc" 32 ) 33 34 // MarketDepth returns the market depth resolver. 35 func (r *VegaResolverRoot) MarketDepth() MarketDepthResolver { 36 return (*myMarketDepthResolver)(r) 37 } 38 39 func (r *VegaResolverRoot) ObservableMarketDepth() ObservableMarketDepthResolver { 40 return (*myObservableMarketDepthResolver)(r) 41 } 42 43 // MarketDepthUpdate returns the market depth update resolver. 44 func (r *VegaResolverRoot) MarketDepthUpdate() MarketDepthUpdateResolver { 45 return (*myMarketDepthUpdateResolver)(r) 46 } 47 48 func (r *VegaResolverRoot) ObservableMarketDepthUpdate() ObservableMarketDepthUpdateResolver { 49 return (*myObservableMarketDepthUpdateResolver)(r) 50 } 51 52 // MarketData returns the market data resolver. 53 func (r *VegaResolverRoot) MarketData() MarketDataResolver { 54 return (*myMarketDataResolver)(r) 55 } 56 57 func (r *VegaResolverRoot) ObservableMarketData() ObservableMarketDataResolver { 58 return (*myObservableMarketDataResolver)(r) 59 } 60 61 // BEGIN: MarketData resolver 62 63 type myMarketDataResolver VegaResolverRoot 64 65 // ActiveProtocolAutomatedPurchase implements MarketDataResolver. 66 func (r *myMarketDataResolver) ActiveProtocolAutomatedPurchase(ctx context.Context, obj *vegapb.MarketData) (*ProtocolAutomatedPurchaseState, error) { 67 if obj.ActiveProtocolAutomatedPurchase != nil { 68 return &ProtocolAutomatedPurchaseState{ 69 ID: obj.ActiveProtocolAutomatedPurchase.Id, 70 OrderID: obj.ActiveProtocolAutomatedPurchase.OrderId, 71 }, nil 72 } 73 return nil, nil 74 } 75 76 func (r *myMarketDataResolver) MarkPriceType(_ context.Context, m *types.MarketData) (CompositePriceType, error) { 77 if m.MarkPriceType == types.CompositePriceType_COMPOSITE_PRICE_TYPE_WEIGHTED { 78 return CompositePriceTypeCompositePriceTypeWeighted, nil 79 } else if m.MarkPriceType == types.CompositePriceType_COMPOSITE_PRICE_TYPE_MEDIAN { 80 return CompositePriceTypeCompositePriceTypeMedian, nil 81 } else { 82 return CompositePriceTypeCompositePriceTypeLastTrade, nil 83 } 84 } 85 86 func (r *myMarketDataResolver) AuctionStart(_ context.Context, m *types.MarketData) (*string, error) { 87 if m.AuctionStart <= 0 { 88 return nil, nil 89 } 90 s := vegatime.Format(vegatime.UnixNano(m.AuctionStart)) 91 return &s, nil 92 } 93 94 func (r *myMarketDataResolver) AuctionEnd(_ context.Context, m *types.MarketData) (*string, error) { 95 if m.AuctionEnd <= 0 { 96 return nil, nil 97 } 98 s := vegatime.Format(vegatime.UnixNano(m.AuctionEnd)) 99 return &s, nil 100 } 101 102 func (r *myMarketDataResolver) IndicativePrice(_ context.Context, m *types.MarketData) (string, error) { 103 return m.IndicativePrice, nil 104 } 105 106 func (r *myMarketDataResolver) IndicativeVolume(_ context.Context, m *types.MarketData) (string, error) { 107 return strconv.FormatUint(m.IndicativeVolume, 10), nil 108 } 109 110 func (r *myMarketDataResolver) BestBidPrice(_ context.Context, m *types.MarketData) (string, error) { 111 return m.BestBidPrice, nil 112 } 113 114 func (r *myMarketDataResolver) BestStaticBidPrice(_ context.Context, m *types.MarketData) (string, error) { 115 return m.BestStaticBidPrice, nil 116 } 117 118 func (r *myMarketDataResolver) BestStaticBidVolume(_ context.Context, m *types.MarketData) (string, error) { 119 return strconv.FormatUint(m.BestStaticBidVolume, 10), nil 120 } 121 122 func (r *myMarketDataResolver) OpenInterest(_ context.Context, m *types.MarketData) (string, error) { 123 return strconv.FormatUint(m.OpenInterest, 10), nil 124 } 125 126 func (r *myMarketDataResolver) BestBidVolume(_ context.Context, m *types.MarketData) (string, error) { 127 return strconv.FormatUint(m.BestBidVolume, 10), nil 128 } 129 130 func (r *myMarketDataResolver) BestOfferPrice(_ context.Context, m *types.MarketData) (string, error) { 131 return m.BestOfferPrice, nil 132 } 133 134 func (r *myMarketDataResolver) BestStaticOfferPrice(_ context.Context, m *types.MarketData) (string, error) { 135 return m.BestStaticOfferPrice, nil 136 } 137 138 func (r *myMarketDataResolver) BestStaticOfferVolume(_ context.Context, m *types.MarketData) (string, error) { 139 return strconv.FormatUint(m.BestStaticOfferVolume, 10), nil 140 } 141 142 func (r *myMarketDataResolver) BestOfferVolume(_ context.Context, m *types.MarketData) (string, error) { 143 return strconv.FormatUint(m.BestOfferVolume, 10), nil 144 } 145 146 func (r *myMarketDataResolver) MidPrice(_ context.Context, m *types.MarketData) (string, error) { 147 return m.MidPrice, nil 148 } 149 150 func (r *myMarketDataResolver) StaticMidPrice(_ context.Context, m *types.MarketData) (string, error) { 151 return m.StaticMidPrice, nil 152 } 153 154 func (r *myMarketDataResolver) MarkPrice(_ context.Context, m *types.MarketData) (string, error) { 155 return m.MarkPrice, nil 156 } 157 158 func (r *myMarketDataResolver) Timestamp(_ context.Context, m *types.MarketData) (string, error) { 159 return vegatime.Format(vegatime.UnixNano(m.Timestamp)), nil 160 } 161 162 func (r *myMarketDataResolver) Commitments(ctx context.Context, m *types.MarketData) (*MarketDataCommitments, error) { 163 // get all the commitments for the given market 164 req := v2.ListLiquidityProvisionsRequest{ 165 MarketId: &m.Market, 166 } 167 res, err := r.tradingDataClientV2.ListLiquidityProvisions(ctx, &req) 168 if err != nil { 169 r.log.Error("tradingData client", logging.Error(err)) 170 return nil, err 171 } 172 173 // now we split all the sells and buys 174 sells := []*types.LiquidityOrderReference{} 175 buys := []*types.LiquidityOrderReference{} 176 177 for _, v := range res.LiquidityProvisions.Edges { 178 sells = append(sells, v.Node.Sells...) 179 buys = append(buys, v.Node.Buys...) 180 } 181 182 return &MarketDataCommitments{ 183 Sells: sells, 184 Buys: buys, 185 }, nil 186 } 187 188 func (r *myMarketDataResolver) PriceMonitoringBounds(ctx context.Context, obj *types.MarketData) ([]*PriceMonitoringBounds, error) { 189 ret := make([]*PriceMonitoringBounds, 0, len(obj.PriceMonitoringBounds)) 190 for _, b := range obj.PriceMonitoringBounds { 191 probability, err := strconv.ParseFloat(b.Trigger.Probability, 64) 192 if err != nil { 193 return nil, err 194 } 195 196 bounds := &PriceMonitoringBounds{ 197 MinValidPrice: b.MinValidPrice, 198 MaxValidPrice: b.MaxValidPrice, 199 Trigger: &PriceMonitoringTrigger{ 200 HorizonSecs: int(b.Trigger.Horizon), 201 Probability: probability, 202 AuctionExtensionSecs: int(b.Trigger.AuctionExtension), 203 }, 204 ReferencePrice: b.ReferencePrice, 205 } 206 ret = append(ret, bounds) 207 } 208 return ret, nil 209 } 210 211 func (r *myMarketDataResolver) Market(ctx context.Context, m *types.MarketData) (*types.Market, error) { 212 return r.r.getMarketByID(ctx, m.Market) 213 } 214 215 func (r *myMarketDataResolver) MarketValueProxy(_ context.Context, m *types.MarketData) (string, error) { 216 return m.MarketValueProxy, nil 217 } 218 219 func (r *myMarketDataResolver) LiquidityProviderFeeShare(_ context.Context, m *types.MarketData) ([]*LiquidityProviderFeeShare, error) { 220 out := make([]*LiquidityProviderFeeShare, 0, len(m.LiquidityProviderFeeShare)) 221 for _, v := range m.LiquidityProviderFeeShare { 222 out = append(out, &LiquidityProviderFeeShare{ 223 Party: &types.Party{Id: v.Party}, 224 EquityLikeShare: v.EquityLikeShare, 225 AverageEntryValuation: v.AverageEntryValuation, 226 AverageScore: v.AverageScore, 227 VirtualStake: v.VirtualStake, 228 }) 229 } 230 return out, nil 231 } 232 233 func (r *myMarketDataResolver) LiquidityProviderSLA(_ context.Context, m *types.MarketData) ([]*LiquidityProviderSLA, error) { 234 out := make([]*LiquidityProviderSLA, 0, len(m.LiquidityProviderSla)) 235 for _, v := range m.LiquidityProviderSla { 236 out = append(out, &LiquidityProviderSLA{ 237 Party: &vegapb.Party{Id: v.Party}, 238 CurrentEpochFractionOfTimeOnBook: v.CurrentEpochFractionOfTimeOnBook, 239 LastEpochFractionOfTimeOnBook: v.LastEpochFractionOfTimeOnBook, 240 LastEpochFeePenalty: v.LastEpochFeePenalty, 241 LastEpochBondPenalty: v.LastEpochBondPenalty, 242 HysteresisPeriodFeePenalties: v.HysteresisPeriodFeePenalties, 243 }) 244 } 245 return out, nil 246 } 247 248 func (r *myMarketDataResolver) NextMarkToMarket(_ context.Context, m *types.MarketData) (string, error) { 249 return vegatime.Format(vegatime.UnixNano(m.NextMarkToMarket)), nil 250 } 251 252 func (r *myMarketDataResolver) NextNetworkCloseout(_ context.Context, m *types.MarketData) (string, error) { 253 if m.NextNetworkCloseout == 0 { 254 return "", nil 255 } 256 return vegatime.Format(vegatime.UnixNano(m.NextNetworkCloseout)), nil 257 } 258 259 func (r *myMarketDataResolver) MarketGrowth(_ context.Context, m *types.MarketData) (string, error) { 260 return m.MarketGrowth, nil 261 } 262 263 func (r *myMarketDataResolver) ProductData(_ context.Context, m *types.MarketData) (ProductData, error) { 264 if m.GetProductData() == nil { 265 return nil, nil 266 } 267 268 switch pd := m.GetProductData().Data.(type) { 269 case *types.ProductData_PerpetualData: 270 return pd.PerpetualData, nil 271 } 272 return nil, nil 273 } 274 275 type myObservableMarketDataResolver myMarketDataResolver 276 277 // ActiveProtocolAutomatedPurchase implements ObservableMarketDataResolver. 278 func (r *myObservableMarketDataResolver) ActiveProtocolAutomatedPurchase(ctx context.Context, obj *vegapb.MarketData) (*ProtocolAutomatedPurchaseState, error) { 279 if obj.ActiveProtocolAutomatedPurchase != nil { 280 return &ProtocolAutomatedPurchaseState{ 281 ID: obj.ActiveProtocolAutomatedPurchase.Id, 282 OrderID: obj.ActiveProtocolAutomatedPurchase.OrderId, 283 }, nil 284 } 285 return nil, nil 286 } 287 288 func (r *myObservableMarketDataResolver) MarketID(ctx context.Context, m *types.MarketData) (string, error) { 289 return m.Market, nil 290 } 291 292 func (r *myObservableMarketDataResolver) AuctionStart(ctx context.Context, m *types.MarketData) (*string, error) { 293 return (*myMarketDataResolver)(r).AuctionStart(ctx, m) 294 } 295 296 func (r *myObservableMarketDataResolver) AuctionEnd(ctx context.Context, m *types.MarketData) (*string, error) { 297 return (*myMarketDataResolver)(r).AuctionEnd(ctx, m) 298 } 299 300 func (r *myObservableMarketDataResolver) IndicativePrice(ctx context.Context, m *types.MarketData) (string, error) { 301 return (*myMarketDataResolver)(r).IndicativePrice(ctx, m) 302 } 303 304 func (r *myObservableMarketDataResolver) IndicativeVolume(ctx context.Context, m *types.MarketData) (string, error) { 305 return (*myMarketDataResolver)(r).IndicativeVolume(ctx, m) 306 } 307 308 func (r *myObservableMarketDataResolver) BestBidPrice(ctx context.Context, m *types.MarketData) (string, error) { 309 return (*myMarketDataResolver)(r).BestBidPrice(ctx, m) 310 } 311 312 func (r *myObservableMarketDataResolver) BestStaticBidPrice(ctx context.Context, m *types.MarketData) (string, error) { 313 return (*myMarketDataResolver)(r).BestStaticBidPrice(ctx, m) 314 } 315 316 func (r *myObservableMarketDataResolver) BestStaticBidVolume(ctx context.Context, m *types.MarketData) (string, error) { 317 return (*myMarketDataResolver)(r).BestStaticBidVolume(ctx, m) 318 } 319 320 func (r *myObservableMarketDataResolver) OpenInterest(_ context.Context, m *types.MarketData) (string, error) { 321 return strconv.FormatUint(m.OpenInterest, 10), nil 322 } 323 324 func (r *myObservableMarketDataResolver) BestBidVolume(ctx context.Context, m *types.MarketData) (string, error) { 325 return (*myMarketDataResolver)(r).BestBidVolume(ctx, m) 326 } 327 328 func (r *myObservableMarketDataResolver) BestOfferPrice(ctx context.Context, m *types.MarketData) (string, error) { 329 return (*myMarketDataResolver)(r).BestOfferPrice(ctx, m) 330 } 331 332 func (r *myObservableMarketDataResolver) BestStaticOfferPrice(ctx context.Context, m *types.MarketData) (string, error) { 333 return (*myMarketDataResolver)(r).BestStaticOfferPrice(ctx, m) 334 } 335 336 func (r *myObservableMarketDataResolver) BestStaticOfferVolume(ctx context.Context, m *types.MarketData) (string, error) { 337 return (*myMarketDataResolver)(r).BestStaticOfferVolume(ctx, m) 338 } 339 340 func (r *myObservableMarketDataResolver) BestOfferVolume(ctx context.Context, m *types.MarketData) (string, error) { 341 return (*myMarketDataResolver)(r).BestOfferVolume(ctx, m) 342 } 343 344 func (r *myObservableMarketDataResolver) MidPrice(ctx context.Context, m *types.MarketData) (string, error) { 345 return (*myMarketDataResolver)(r).MidPrice(ctx, m) 346 } 347 348 func (r *myObservableMarketDataResolver) StaticMidPrice(ctx context.Context, m *types.MarketData) (string, error) { 349 return (*myMarketDataResolver)(r).StaticMidPrice(ctx, m) 350 } 351 352 func (r *myObservableMarketDataResolver) MarkPrice(ctx context.Context, m *types.MarketData) (string, error) { 353 return (*myMarketDataResolver)(r).MarkPrice(ctx, m) 354 } 355 356 func (r *myObservableMarketDataResolver) MarkPriceType(_ context.Context, m *types.MarketData) (CompositePriceType, error) { 357 if m.MarkPriceType == types.CompositePriceType_COMPOSITE_PRICE_TYPE_WEIGHTED { 358 return CompositePriceTypeCompositePriceTypeWeighted, nil 359 } else if m.MarkPriceType == types.CompositePriceType_COMPOSITE_PRICE_TYPE_MEDIAN { 360 return CompositePriceTypeCompositePriceTypeMedian, nil 361 } else { 362 return CompositePriceTypeCompositePriceTypeLastTrade, nil 363 } 364 } 365 366 func (r *myObservableMarketDataResolver) Timestamp(ctx context.Context, m *types.MarketData) (string, error) { 367 return (*myMarketDataResolver)(r).Timestamp(ctx, m) 368 } 369 370 func (r *myObservableMarketDataResolver) PriceMonitoringBounds(ctx context.Context, obj *types.MarketData) ([]*PriceMonitoringBounds, error) { 371 return (*myMarketDataResolver)(r).PriceMonitoringBounds(ctx, obj) 372 } 373 374 func (r *myObservableMarketDataResolver) ProductData(_ context.Context, m *types.MarketData) (ProductData, error) { 375 if m.GetProductData() == nil { 376 return nil, nil 377 } 378 379 switch pd := m.GetProductData().Data.(type) { 380 case *types.ProductData_PerpetualData: 381 return pd.PerpetualData, nil 382 } 383 return nil, nil 384 } 385 386 // ExtensionTrigger same as Trigger. 387 func (r *myObservableMarketDataResolver) ExtensionTrigger(ctx context.Context, m *types.MarketData) (vega.AuctionTrigger, error) { 388 return m.ExtensionTrigger, nil 389 } 390 391 func (r *myObservableMarketDataResolver) MarketValueProxy(ctx context.Context, m *types.MarketData) (string, error) { 392 return (*myMarketDataResolver)(r).MarketValueProxy(ctx, m) 393 } 394 395 func (r *myObservableMarketDataResolver) LiquidityProviderFeeShare(ctx context.Context, m *types.MarketData) ([]*ObservableLiquidityProviderFeeShare, error) { 396 out := make([]*ObservableLiquidityProviderFeeShare, 0, len(m.LiquidityProviderFeeShare)) 397 for _, v := range m.LiquidityProviderFeeShare { 398 out = append(out, &ObservableLiquidityProviderFeeShare{ 399 PartyID: v.Party, 400 EquityLikeShare: v.EquityLikeShare, 401 AverageEntryValuation: v.AverageEntryValuation, 402 AverageScore: v.AverageScore, 403 }) 404 } 405 return out, nil 406 } 407 408 func (r *myObservableMarketDataResolver) LiquidityProviderSLA(ctx context.Context, m *types.MarketData) ([]*ObservableLiquidityProviderSLA, error) { 409 out := make([]*ObservableLiquidityProviderSLA, 0, len(m.LiquidityProviderSla)) 410 for _, v := range m.LiquidityProviderSla { 411 out = append(out, &ObservableLiquidityProviderSLA{ 412 Party: v.Party, 413 CurrentEpochFractionOfTimeOnBook: v.CurrentEpochFractionOfTimeOnBook, 414 LastEpochFractionOfTimeOnBook: v.LastEpochFractionOfTimeOnBook, 415 LastEpochFeePenalty: v.LastEpochFeePenalty, 416 LastEpochBondPenalty: v.LastEpochBondPenalty, 417 HysteresisPeriodFeePenalties: v.HysteresisPeriodFeePenalties, 418 }) 419 } 420 return out, nil 421 } 422 423 func (r *myObservableMarketDataResolver) NextMarkToMarket(ctx context.Context, m *types.MarketData) (string, error) { 424 return (*myMarketDataResolver)(r).NextMarkToMarket(ctx, m) 425 } 426 427 func (r *myObservableMarketDataResolver) NextNetworkCloseout(ctx context.Context, m *types.MarketData) (string, error) { 428 return (*myMarketDataResolver)(r).NextNetworkCloseout(ctx, m) 429 } 430 431 func (r *myObservableMarketDataResolver) MarketGrowth(ctx context.Context, m *types.MarketData) (string, error) { 432 return (*myMarketDataResolver)(r).MarketGrowth(ctx, m) 433 } 434 435 // END: MarketData resolver 436 437 // BEGIN: Market Depth Resolver 438 439 type myMarketDepthResolver VegaResolverRoot 440 441 func (r *myMarketDepthResolver) LastTrade(ctx context.Context, md *types.MarketDepth) (*types.Trade, error) { 442 if md == nil { 443 return nil, errors.New("invalid market depth") 444 } 445 446 req := v2.GetLastTradeRequest{MarketId: md.MarketId} 447 res, err := r.tradingDataClientV2.GetLastTrade(ctx, &req) 448 if err != nil { 449 r.log.Error("tradingData client", logging.Error(err)) 450 return nil, err 451 } 452 return res.Trade, nil 453 } 454 455 func (r *myMarketDepthResolver) SequenceNumber(ctx context.Context, md *types.MarketDepth) (string, error) { 456 return strconv.FormatUint(md.SequenceNumber, 10), nil 457 } 458 459 func (r *myMarketDepthResolver) Market(ctx context.Context, md *types.MarketDepth) (*types.Market, error) { 460 return r.r.getMarketByID(ctx, md.MarketId) 461 } 462 463 type myObservableMarketDepthResolver myMarketDepthResolver 464 465 func (r *myObservableMarketDepthResolver) LastTrade(ctx context.Context, md *types.MarketDepth) (*MarketDepthTrade, error) { 466 if md == nil { 467 return nil, errors.New("invalid market depth") 468 } 469 470 req := v2.GetLastTradeRequest{MarketId: md.MarketId} 471 res, err := r.tradingDataClientV2.GetLastTrade(ctx, &req) 472 if err != nil { 473 r.log.Error("tradingData client", logging.Error(err)) 474 return nil, err 475 } 476 return &MarketDepthTrade{ID: res.Trade.Id, Price: res.Trade.Price, Size: strconv.FormatUint(res.Trade.Size, 10)}, nil 477 } 478 479 func (r *myObservableMarketDepthResolver) SequenceNumber(ctx context.Context, md *types.MarketDepth) (string, error) { 480 return (*myMarketDepthResolver)(r).SequenceNumber(ctx, md) 481 } 482 483 // END: Market Depth Resolver 484 485 // BEGIN: Market Depth Update Resolver 486 487 type myMarketDepthUpdateResolver VegaResolverRoot 488 489 func (r *myMarketDepthUpdateResolver) SequenceNumber(ctx context.Context, md *types.MarketDepthUpdate) (string, error) { 490 return strconv.FormatUint(md.SequenceNumber, 10), nil 491 } 492 493 func (r *myMarketDepthUpdateResolver) PreviousSequenceNumber(ctx context.Context, md *types.MarketDepthUpdate) (string, error) { 494 return strconv.FormatUint(md.PreviousSequenceNumber, 10), nil 495 } 496 497 func (r *myMarketDepthUpdateResolver) Market(ctx context.Context, md *types.MarketDepthUpdate) (*types.Market, error) { 498 return r.r.getMarketByID(ctx, md.MarketId) 499 } 500 501 type myObservableMarketDepthUpdateResolver myMarketDepthUpdateResolver 502 503 func (r *myObservableMarketDepthUpdateResolver) SequenceNumber(ctx context.Context, md *types.MarketDepthUpdate) (string, error) { 504 return (*myMarketDepthUpdateResolver)(r).SequenceNumber(ctx, md) 505 } 506 507 func (r *myObservableMarketDepthUpdateResolver) PreviousSequenceNumber(ctx context.Context, md *types.MarketDepthUpdate) (string, error) { 508 return (*myMarketDepthUpdateResolver)(r).PreviousSequenceNumber(ctx, md) 509 } 510 511 // END: Market Depth Update Resolver 512 513 func (r *mySubscriptionResolver) MarketsDepth(ctx context.Context, marketIds []string) (<-chan []*types.MarketDepth, error) { 514 req := &v2.ObserveMarketsDepthRequest{ 515 MarketIds: marketIds, 516 } 517 stream, err := r.tradingDataClientV2.ObserveMarketsDepth(ctx, req) 518 if err != nil { 519 return nil, err 520 } 521 522 return grpcStreamToGraphQlChannel[*v2.ObserveMarketsDepthResponse](ctx, r.log, "marketsDepth", stream, 523 func(md *v2.ObserveMarketsDepthResponse) []*types.MarketDepth { 524 return md.MarketDepth 525 }), nil 526 } 527 528 func (r *mySubscriptionResolver) MarketsDepthUpdate(ctx context.Context, marketIDs []string) (<-chan []*types.MarketDepthUpdate, error) { 529 req := &v2.ObserveMarketsDepthUpdatesRequest{ 530 MarketIds: marketIDs, 531 } 532 stream, err := r.tradingDataClientV2.ObserveMarketsDepthUpdates(ctx, req) 533 if err != nil { 534 return nil, err 535 } 536 537 return grpcStreamToGraphQlChannel[*v2.ObserveMarketsDepthUpdatesResponse](ctx, r.log, "marketsDepthUpdate", stream, 538 func(md *v2.ObserveMarketsDepthUpdatesResponse) []*types.MarketDepthUpdate { 539 return md.Update 540 }), nil 541 } 542 543 func (r *mySubscriptionResolver) MarketsData(ctx context.Context, marketIds []string) (<-chan []*types.MarketData, error) { 544 req := &v2.ObserveMarketsDataRequest{ 545 MarketIds: marketIds, 546 } 547 stream, err := r.tradingDataClientV2.ObserveMarketsData(ctx, req) 548 if err != nil { 549 return nil, err 550 } 551 552 return grpcStreamToGraphQlChannel[*v2.ObserveMarketsDataResponse](ctx, r.log, "marketsdata", stream, 553 func(md *v2.ObserveMarketsDataResponse) []*types.MarketData { 554 return md.MarketData 555 }), nil 556 } 557 558 type grpcStream[T any] interface { 559 Recv() (T, error) 560 grpc.ClientStream 561 } 562 563 func grpcStreamToGraphQlChannel[T any, Y any](ctx context.Context, log *logging.Logger, observableType string, stream grpcStream[T], grpcStreamTypeToGraphQlType func(T) Y) chan Y { 564 // should be just a wrapped version of ctx, but let's check both 565 sCtx := stream.Context() 566 c := make(chan Y) 567 go func() { 568 defer func() { 569 stream.CloseSend() 570 close(c) 571 }() 572 for { 573 md, err := stream.Recv() 574 if err == io.EOF { 575 log.Error(observableType+": stream closed by server", logging.Error(err)) 576 break 577 } 578 if err != nil { 579 log.Error(observableType+": stream closed", logging.Error(err)) 580 break 581 } 582 select { 583 case c <- grpcStreamTypeToGraphQlType(md): 584 log.Debugf("%s: data sent", observableType) 585 continue 586 case <-sCtx.Done(): 587 log.Debugf("%s: stream closed by server", observableType) 588 break 589 case <-ctx.Done(): 590 log.Debugf("%s: stream closed", observableType) 591 break 592 } 593 } 594 }() 595 return c 596 }