github.com/polygon-io/client-go@v1.16.4/rest/snapshot_test.go (about) 1 package polygon_test 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "testing" 8 9 "github.com/jarcoal/httpmock" 10 polygon "github.com/polygon-io/client-go/rest" 11 "github.com/polygon-io/client-go/rest/models" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/require" 14 ) 15 16 var snapshot1 = `{ 17 "day": { 18 "c": 20.506, 19 "h": 20.64, 20 "l": 20.506, 21 "o": 20.64, 22 "v": 37216, 23 "vw": 20.616 24 }, 25 "lastQuote": { 26 "P": 20.6, 27 "p": 20.5, 28 "S": 22, 29 "s": 13, 30 "t": 1605192959994246100 31 }, 32 "lastTrade": { 33 "c": [ 34 14, 35 41 36 ], 37 "i": "71675577320245", 38 "p": 20.506, 39 "s": 2416, 40 "t": 1605192894630916600, 41 "x": 4 42 }, 43 "min": { 44 "av": 37216.0, 45 "c": 20.506, 46 "h": 20.506, 47 "l": 20.506, 48 "o": 20.506, 49 "v": 5000, 50 "vw": 20.5105, 51 "t": 1684428060000, 52 "n": 4 53 }, 54 "prevDay": { 55 "c": 20.63, 56 "h": 21, 57 "l": 20.5, 58 "o": 20.79, 59 "v": 292738, 60 "vw": 20.6939 61 }, 62 "ticker": "BCAT", 63 "todaysChange": -0.124, 64 "todaysChangePerc": -0.601, 65 "updated": 1605192894630916600, 66 "fmv": 20.506 67 }` 68 69 var snapshot2 = `{ 70 "day": { 71 "c": 313.225, 72 "h": 314.35, 73 "l": 309.71, 74 "o": 310.09, 75 "v": 6322693, 76 "vw": 312.6791 77 }, 78 "lastQuote": { 79 "P": 313.13, 80 "p": 313.11, 81 "S": 4, 82 "s": 2, 83 "t": 1649083047683654000 84 }, 85 "lastTrade": { 86 "i": "23432", 87 "p": 313.1296, 88 "s": 100.0, 89 "t": 1649083047682204000, 90 "x": 4 91 }, 92 "min": { 93 "av": 6321712, 94 "c": 313.1826, 95 "h": 313.19, 96 "l": 312.66, 97 "o": 312.78, 98 "v": 54315, 99 "vw": 312.9441, 100 "t": 1684428060000, 101 "n": 4 102 }, 103 "prevDay": { 104 "c": 309.42, 105 "h": 310.13, 106 "l": 305.54, 107 "o": 309.37, 108 "v": 27101029, 109 "vw": 308.0485 110 }, 111 "ticker": "MSFT", 112 "todaysChange": 3.71, 113 "todaysChangePerc": 1.199, 114 "updated": 1649083047682204000, 115 "fmv": 313.1296 116 }` 117 118 func TestListSnapshotAllTickers(t *testing.T) { 119 c := polygon.New("API_KEY") 120 121 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 122 defer httpmock.DeactivateAndReset() 123 124 expectedResponse := `{ 125 "status": "OK", 126 "count": 2, 127 "tickers": [ 128 ` + indent(true, snapshot1, "\t\t") + `, 129 ` + indent(true, snapshot2, "\t\t") + ` 130 ] 131 }` 132 133 registerResponder("https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/tickers?tickers=AAPL%2CMSFT", expectedResponse) 134 res, err := c.GetAllTickersSnapshot(context.Background(), models.GetAllTickersSnapshotParams{ 135 Locale: "us", 136 MarketType: "stocks", 137 }.WithTickers("AAPL,MSFT")) 138 assert.Nil(t, err) 139 140 var expect models.GetAllTickersSnapshotResponse 141 err = json.Unmarshal([]byte(expectedResponse), &expect) 142 assert.Nil(t, err) 143 assert.Equal(t, &expect, res) 144 } 145 146 func TestGetTickerSnapshot(t *testing.T) { 147 c := polygon.New("API_KEY") 148 149 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 150 defer httpmock.DeactivateAndReset() 151 152 expectedResponse := `{ 153 "status": "OK", 154 "count": 2, 155 "ticker": ` + indent(false, snapshot1, "\t") + ` 156 }` 157 158 registerResponder("https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/tickers/AAPL", expectedResponse) 159 res, err := c.GetTickerSnapshot(context.Background(), &models.GetTickerSnapshotParams{ 160 Ticker: "AAPL", 161 Locale: "us", 162 MarketType: "stocks", 163 }) 164 assert.Nil(t, err) 165 166 var expect models.GetTickerSnapshotResponse 167 err = json.Unmarshal([]byte(expectedResponse), &expect) 168 assert.Nil(t, err) 169 assert.Equal(t, &expect, res) 170 } 171 172 func TestGetGainersLosersSnapshot(t *testing.T) { 173 c := polygon.New("API_KEY") 174 175 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 176 defer httpmock.DeactivateAndReset() 177 178 expectedResponse := `{ 179 "status": "OK", 180 "count": 2, 181 "tickers": [ 182 ` + indent(true, snapshot1, "\t\t") + `, 183 ` + indent(true, snapshot2, "\t\t") + ` 184 ] 185 }` 186 187 registerResponder("https://api.polygon.io/v2/snapshot/locale/us/markets/stocks/gainers", expectedResponse) 188 res, err := c.GetGainersLosersSnapshot(context.Background(), &models.GetGainersLosersSnapshotParams{ 189 Locale: "us", 190 MarketType: "stocks", 191 Direction: "gainers", 192 }) 193 assert.Nil(t, err) 194 195 var expect models.GetGainersLosersSnapshotResponse 196 err = json.Unmarshal([]byte(expectedResponse), &expect) 197 assert.Nil(t, err) 198 assert.Equal(t, &expect, res) 199 } 200 201 func TestGetOptionContractSnapshot(t *testing.T) { 202 c := polygon.New("API_KEY") 203 204 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 205 defer httpmock.DeactivateAndReset() 206 207 expectedResponse := `{ 208 "status": "OK", 209 "request_id": "d9ff18dac69f55c218f69e4753706acd", 210 "results": { 211 "break_even_price": 171.075, 212 "day": { 213 "change": -1.05, 214 "change_percent": -4.67, 215 "close": 21.4, 216 "high": 22.49, 217 "last_updated": 1636520400000000000, 218 "low": 21.35, 219 "open": 22.49, 220 "previous_close": 22.45, 221 "volume": 37, 222 "vwap": 21.6741 223 }, 224 "details": { 225 "contract_type": "call", 226 "exercise_style": "american", 227 "expiration_date": "2023-06-16", 228 "shares_per_contract": 100, 229 "strike_price": 150, 230 "ticker": "O:AAPL230616C00150000" 231 }, 232 "greeks": { 233 "delta": 0.5520187372272933, 234 "gamma": 0.00706756515659829, 235 "theta": -0.018532772783847958, 236 "vega": 0.7274811132998142 237 }, 238 "implied_volatility": 0.3048997097864957, 239 "last_quote": { 240 "ask": 21.25, 241 "ask_size": 110, 242 "bid": 20.9, 243 "bid_size": 172, 244 "last_updated": 1636573458756383500, 245 "midpoint": 21.075, 246 "timeframe": "REAL-TIME" 247 }, 248 "last_trade": { 249 "sip_timestamp": 1676573362154648300, 250 "conditions": [ 251 209 252 ], 253 "price": 110.9, 254 "size": 10, 255 "exchange": 308, 256 "timeframe": "REAL-TIME" 257 }, 258 "open_interest": 8921, 259 "underlying_asset": { 260 "change_to_break_even": 23.123999999999995, 261 "last_updated": 1636573459862384600, 262 "price": 147.951, 263 "ticker": "AAPL", 264 "timeframe": "REAL-TIME" 265 }, 266 "fmv": 21.075 267 } 268 }` 269 270 registerResponder("https://api.polygon.io/v3/snapshot/options/AAPL/O:AAPL230616C00150000", expectedResponse) 271 res, err := c.GetOptionContractSnapshot(context.Background(), &models.GetOptionContractSnapshotParams{ 272 UnderlyingAsset: "AAPL", 273 OptionContract: "O:AAPL230616C00150000", 274 }) 275 assert.Nil(t, err) 276 277 var expect models.GetOptionContractSnapshotResponse 278 err = json.Unmarshal([]byte(expectedResponse), &expect) 279 assert.Nil(t, err) 280 assert.Equal(t, &expect, res) 281 } 282 283 func TestListOptionsChainSnapshot(t *testing.T) { 284 c := polygon.New("API_KEY") 285 286 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 287 defer httpmock.DeactivateAndReset() 288 289 chain1 := `{ 290 "break_even_price": 162.375, 291 "day": { 292 "change": 0, 293 "change_percent": 0, 294 "close": 79.35, 295 "high": 79.35, 296 "last_updated": 1672434000000, 297 "low": 79.3, 298 "open": 79.3, 299 "previous_close": 79.35, 300 "volume": 22, 301 "vwap": 79.325 302 }, 303 "details": { 304 "contract_type": "call", 305 "exercise_style": "american", 306 "expiration_date": "2023-01-06", 307 "shares_per_contract": 100, 308 "strike_price": 50, 309 "ticker": "O:AAPL230106C00050000" 310 }, 311 "greeks": {}, 312 "last_quote": { 313 "ask": 75.05, 314 "ask_size": 48, 315 "bid": 74.85, 316 "bid_size": 43, 317 "last_updated": 1672775256862312000, 318 "midpoint": 112.375, 319 "timeframe": "DELAYED" 320 }, 321 "last_trade": { 322 "sip_timestamp": 1676573362154648300, 323 "conditions": [ 324 209 325 ], 326 "price": 110.9, 327 "size": 10, 328 "exchange": 308, 329 "timeframe": "REAL-TIME" 330 }, 331 "open_interest": 5, 332 "underlying_asset": { 333 "change_to_break_even": 37.435, 334 "last_updated": 1672775257417223400, 335 "price": 124.94, 336 "ticker": "AAPL", 337 "timeframe": "DELAYED" 338 }, 339 "fmv": 75.06 340 }` 341 chain2 := `{ 342 "break_even_price": 162.375, 343 "day": { 344 "change": 0, 345 "change_percent": 0, 346 "close": 79.35, 347 "high": 79.35, 348 "last_updated": 1672434000000, 349 "low": 79.3, 350 "open": 79.3, 351 "previous_close": 79.35, 352 "volume": 22, 353 "vwap": 79.325 354 }, 355 "details": { 356 "contract_type": "call", 357 "exercise_style": "american", 358 "expiration_date": "2023-01-06", 359 "shares_per_contract": 100, 360 "strike_price": 50, 361 "ticker": "O:AAPL230106C00050000" 362 }, 363 "greeks": {}, 364 "last_quote": { 365 "ask": 75.05, 366 "ask_size": 48, 367 "bid": 74.85, 368 "bid_size": 43, 369 "last_updated": 1672775256862312000, 370 "midpoint": 112.375, 371 "timeframe": "DELAYED" 372 }, 373 "last_trade": { 374 "sip_timestamp": 1676573362154648300, 375 "conditions": [ 376 209 377 ], 378 "price": 110.9, 379 "size": 10, 380 "exchange": 308, 381 "timeframe": "REAL-TIME" 382 }, 383 "open_interest": 5, 384 "underlying_asset": { 385 "change_to_break_even": 37.435, 386 "last_updated": 1672775257417223400, 387 "price": 124.94, 388 "ticker": "AAPL", 389 "timeframe": "DELAYED" 390 }, 391 "fmv": 75.06 392 }` 393 chain3 := `{ 394 "break_even_price": 162.375, 395 "day": { 396 "change": 0, 397 "change_percent": 0, 398 "close": 79.35, 399 "high": 79.35, 400 "last_updated": 1672434000000, 401 "low": 79.3, 402 "open": 79.3, 403 "previous_close": 79.35, 404 "volume": 22, 405 "vwap": 79.325 406 }, 407 "details": { 408 "contract_type": "call", 409 "exercise_style": "american", 410 "expiration_date": "2023-01-06", 411 "shares_per_contract": 100, 412 "strike_price": 50, 413 "ticker": "O:AAPL230106C00050000" 414 }, 415 "greeks": {}, 416 "last_quote": { 417 "ask": 75.05, 418 "ask_size": 48, 419 "bid": 74.85, 420 "bid_size": 43, 421 "last_updated": 1672775256862312000, 422 "midpoint": 112.375, 423 "timeframe": "DELAYED" 424 }, 425 "last_trade": { 426 "sip_timestamp": 1676573362154648300, 427 "conditions": [ 428 209 429 ], 430 "price": 110.9, 431 "size": 10, 432 "exchange": 308, 433 "timeframe": "REAL-TIME" 434 }, 435 "open_interest": 5, 436 "underlying_asset": { 437 "change_to_break_even": 37.435, 438 "last_updated": 1672775257417223400, 439 "price": 124.94, 440 "ticker": "AAPL", 441 "timeframe": "DELAYED" 442 }, 443 "fmv": 75.06 444 }` 445 446 expectedResponse := `{ 447 "results": [ 448 ` + indent(true, chain1, "\t\t") + `, 449 ` + indent(true, chain2, "\t\t") + `, 450 ` + indent(true, chain3, "\t\t") + ` 451 ], 452 "status": "OK", 453 "request_id": "0d350849-a2a8-43c5-8445-9c6f55d371e6", 454 "next_url": "https://api.polygon.io/v3/snapshot/options/AAPL?cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg" 455 }` 456 457 registerResponder("https://api.polygon.io/v3/snapshot/options/AAPL", expectedResponse) 458 registerResponder("https://api.polygon.io/v3/snapshot/options/AAPL?cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg", "{}") 459 460 iter := c.ListOptionsChainSnapshot(context.Background(), &models.ListOptionsChainParams{UnderlyingAsset: "AAPL"}) 461 462 // iter creation 463 assert.Nil(t, iter.Err()) 464 assert.NotNil(t, iter.Item()) 465 466 // first item 467 assert.True(t, iter.Next()) 468 assert.Nil(t, iter.Err()) 469 var expect1 models.OptionContractSnapshot 470 err := json.Unmarshal([]byte(chain1), &expect1) 471 assert.Nil(t, err) 472 assert.Equal(t, expect1, iter.Item()) 473 474 // second item 475 assert.True(t, iter.Next()) 476 assert.Nil(t, iter.Err()) 477 var expect2 models.OptionContractSnapshot 478 err = json.Unmarshal([]byte(chain2), &expect2) 479 assert.Nil(t, err) 480 assert.Equal(t, expect2, iter.Item()) 481 482 // third item 483 assert.True(t, iter.Next()) 484 assert.Nil(t, iter.Err()) 485 var expect3 models.OptionContractSnapshot 486 err = json.Unmarshal([]byte(chain3), &expect3) 487 assert.Nil(t, err) 488 assert.Equal(t, expect3, iter.Item()) 489 490 // end of list 491 assert.False(t, iter.Next()) 492 assert.Nil(t, iter.Err()) 493 } 494 495 func TestGetCryptoFullBookSnapshot(t *testing.T) { 496 c := polygon.New("API_KEY") 497 498 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 499 defer httpmock.DeactivateAndReset() 500 501 expectedResponse := `{ 502 "status": "OK", 503 "data": { 504 "askCount": 593.1412981600005, 505 "asks": [ 506 { 507 "p": 11454, 508 "x": { 509 "2": 1 510 } 511 }, 512 { 513 "p": 11455, 514 "x": { 515 "2": 1 516 } 517 } 518 ], 519 "bidCount": 694.951789670001, 520 "bids": [ 521 { 522 "p": 16303.17, 523 "x": { 524 "1": 2 525 } 526 }, 527 { 528 "p": 16302.94, 529 "x": { 530 "1": 0.02859424, 531 "6": 0.023455 532 } 533 } 534 ], 535 "spread": -4849.17, 536 "ticker": "X:BTCUSD", 537 "updated": 1605295074162 538 } 539 }` 540 541 registerResponder("https://api.polygon.io/v2/snapshot/locale/global/markets/crypto/tickers/X:BTCUSD/book", expectedResponse) 542 res, err := c.GetCryptoFullBookSnapshot(context.Background(), &models.GetCryptoFullBookSnapshotParams{ 543 Ticker: "X:BTCUSD", 544 }) 545 assert.Nil(t, err) 546 547 var expect models.GetCryptoFullBookSnapshotResponse 548 err = json.Unmarshal([]byte(expectedResponse), &expect) 549 assert.Nil(t, err) 550 assert.Equal(t, &expect, res) 551 } 552 553 func TestGetIndicesSnapshot(t *testing.T) { 554 c := polygon.New("API_KEY") 555 556 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 557 defer httpmock.DeactivateAndReset() 558 expectedIndicesSnapshotResponse := `{ 559 "results": [ 560 { 561 "value": 1326.17, 562 "name": "Dow Jones Americas Health Care Index", 563 "ticker": "I:A1HCR", 564 "market_status": "open", 565 "type": "indices", 566 "session": { 567 "change": 47.07, 568 "change_percent": 3.68, 569 "close": 1282.67, 570 "high": 1288.89, 571 "low": 1282.25, 572 "open": 1283.33, 573 "previous_close": 1279.1000000000001 574 } 575 }, 576 { 577 "value": 3918.32, 578 "name": "Standard & Poor's 500", 579 "ticker": "I:SPX", 580 "market_status": "open", 581 "type": "indices", 582 "session": { 583 "change": 5.56, 584 "change_percent": 0.142, 585 "close": 3926.36, 586 "high": 3927.38, 587 "low": 3878.1, 588 "open": 3914.13, 589 "previous_close": 3912.76 590 } 591 } 592 ], 593 "status": "OK", 594 "request_id": "5ad18f153c5aa4a543cc10aeb9245622" 595 } 596 597 ` 598 599 expectedGetIndicesSnapshotUrl := "https://api.polygon.io/v3/snapshot/indices?ticker.any_of=I%3AA1HCR%2CI%3ASPX" 600 registerResponder(expectedGetIndicesSnapshotUrl, expectedIndicesSnapshotResponse) 601 tickerAnyOf := []string{"I:A1HCR", "I:SPX"} 602 603 res, err := c.GetIndicesSnapshot(context.Background(), models.GetIndicesSnapshotParams{}.WithTickerAnyOf(tickerAnyOf...)) 604 assert.Nil(t, err) 605 606 var expect models.GetIndicesSnapshotResponse 607 err = json.Unmarshal([]byte(expectedIndicesSnapshotResponse), &expect) 608 assert.Nil(t, err) 609 assert.Equal(t, &expect, res) 610 } 611 612 func TestListUniversalSnapshots(t *testing.T) { 613 c := polygon.New("API_KEY") 614 615 httpmock.ActivateNonDefault(c.HTTP.GetClient()) 616 defer httpmock.DeactivateAndReset() 617 618 tt := []struct { 619 name string 620 haveParams *models.ListUniversalSnapshotsParams 621 haveRequestURL string 622 wantResponse string 623 testData []string 624 wantErr bool 625 }{ 626 { 627 name: "Stock tickers", 628 haveParams: models.ListUniversalSnapshotsParams{}.WithTickerAnyOf("AAPL,META,F"), 629 haveRequestURL: "https://api.polygon.io/v3/snapshot?ticker.any_of=AAPL%2CMETA%2CF", 630 wantResponse: `{ 631 "results": [ 632 ` + indent(true, stockSnapshotsTestData[0], "\t\t") + `, 633 ` + indent(true, stockSnapshotsTestData[1], "\t\t") + `, 634 ` + indent(true, stockSnapshotsTestData[2], "\t\t") + ` 635 ], 636 "status": "OK", 637 "request_id": "0d350849-a2a8-43c5-8445-9c6f55d371e6", 638 "next_url": "https://api.polygon.io/v3/snapshot/cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg" 639 }`, 640 testData: stockSnapshotsTestData, 641 wantErr: false, 642 }, 643 { 644 name: "Options tickers", 645 haveParams: models.ListUniversalSnapshotsParams{}.WithTickerAnyOf("O:AAPL230512C00050000,O:META230512C00020000,O:F230512C00005000"), 646 haveRequestURL: "https://api.polygon.io/v3/snapshot?ticker.any_of=O%3AAAPL230512C00050000%2CO%3AMETA230512C00020000%2CO%3AF230512C00005000", 647 wantResponse: `{ 648 "results": [ 649 ` + indent(true, optionsSnapshotsTestData[0], "\t\t") + `, 650 ` + indent(true, optionsSnapshotsTestData[1], "\t\t") + `, 651 ` + indent(true, optionsSnapshotsTestData[2], "\t\t") + ` 652 ], 653 "status": "OK", 654 "request_id": "0d350849-a2a8-43c5-8445-9c6f55d371e6", 655 "next_url": "https://api.polygon.io/v3/snapshot/cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg" 656 }`, 657 testData: optionsSnapshotsTestData, 658 }, 659 { 660 name: "Crypto Snapshot", 661 haveParams: models.ListUniversalSnapshotsParams{}.WithTickerAnyOf("X:BTCUSD,X:ETHUSD,X:FLOWUSD"), 662 haveRequestURL: "https://api.polygon.io/v3/snapshot?ticker.any_of=X%3ABTCUSD%2CX%3AETHUSD%2CX%3AFLOWUSD", 663 wantResponse: `{ 664 "results": [ 665 ` + indent(true, cryptoSnapshotsTestData[0], "\t\t") + `, 666 ` + indent(true, cryptoSnapshotsTestData[1], "\t\t") + `, 667 ` + indent(true, cryptoSnapshotsTestData[2], "\t\t") + ` 668 ], 669 "status": "OK", 670 "request_id": "0d350849-a2a8-43c5-8445-9c6f55d371e6", 671 "next_url": "https://api.polygon.io/v3/snapshot/cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg" 672 }`, 673 testData: cryptoSnapshotsTestData, 674 }, 675 { 676 name: "Forex Snapshot", 677 haveParams: models.ListUniversalSnapshotsParams{}.WithTickerAnyOf("C:USDCAD,C:USDEUR,C:USDAUD"), 678 haveRequestURL: "https://api.polygon.io/v3/snapshot?ticker.any_of=C%3AUSDCAD%2CC%3AUSDEUR%2CC%3AUSDAUD", 679 wantResponse: `{ 680 "results": [ 681 ` + indent(true, forexSnapshotTestData[0], "\t\t") + `, 682 ` + indent(true, forexSnapshotTestData[1], "\t\t") + `, 683 ` + indent(true, forexSnapshotTestData[2], "\t\t") + ` 684 ], 685 "status": "OK", 686 "request_id": "0d350849-a2a8-43c5-8445-9c6f55d371e6", 687 "next_url": "https://api.polygon.io/v3/snapshot/cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg" 688 }`, 689 testData: forexSnapshotTestData, 690 }, 691 { 692 name: "Indices Snapshot", 693 haveParams: models.ListUniversalSnapshotsParams{}.WithTickerAnyOf("I:SPX,I:DJI,I:A1BSC"), 694 haveRequestURL: "https://api.polygon.io/v3/snapshot?ticker.any_of=I%3ASPX%2CI%3ADJI%2CI%3AA1BSC", 695 wantResponse: `{ 696 "results": [ 697 ` + indent(true, indicesSnapshotTestData[0], "\t\t") + `, 698 ` + indent(true, indicesSnapshotTestData[1], "\t\t") + `, 699 ` + indent(true, indicesSnapshotTestData[2], "\t\t") + ` 700 ], 701 "status": "OK", 702 "request_id": "0d350849-a2a8-43c5-8445-9c6f55d371e6", 703 "next_url": "https://api.polygon.io/v3/snapshot/cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg" 704 }`, 705 testData: indicesSnapshotTestData, 706 }, 707 { 708 name: "Partial success (200/OK with an error message in the body)", 709 haveParams: models.ListUniversalSnapshotsParams{}.WithTickerAnyOf("AAPL,APx"), 710 haveRequestURL: "https://api.polygon.io/v3/snapshot?ticker.any_of=AAPL%2CAPx", 711 wantResponse: `{ 712 "results": [ 713 ` + indent(true, partialSuccessWithStocksTestData[0], "\t\t") + `, 714 ` + indent(true, partialSuccessWithStocksTestData[1], "\t\t") + ` 715 ], 716 "status": "OK", 717 "request_id": "0d350849-a2a8-43c5-8445-9c6f55d371e6", 718 "next_url": "https://api.polygon.io/v3/snapshot/cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg" 719 }`, 720 testData: partialSuccessWithStocksTestData, 721 wantErr: true, 722 }, 723 } 724 725 for _, tc := range tt { 726 t.Run(tc.name, func(t *testing.T) { 727 728 registerResponder(tc.haveRequestURL, tc.wantResponse) 729 registerResponder("https://api.polygon.io/v3/snapshot/cursor=YXA9MSZhcz0mbGltaXQ9MSZzb3J0PXRpY2tlcg", "{}") 730 731 iter := c.ListUniversalSnapshots( 732 context.Background(), 733 tc.haveParams, 734 ) 735 736 require.NoError(t, iter.Err()) 737 require.NotNil(t, iter.Item()) 738 739 var iterCount int 740 for iter.Next() { 741 var gotSnapshot models.SnapshotResponseModel 742 err := json.Unmarshal([]byte(tc.testData[iterCount]), &gotSnapshot) 743 require.Nil(t, err) 744 745 require.Nil(t, iter.Err()) 746 assert.Equal(t, gotSnapshot, iter.Item()) 747 iterCount++ 748 } 749 750 assert.Equal(t, len(tc.testData), iterCount, fmt.Sprintf("expected %d results", len(tc.testData))) 751 assert.False(t, iter.Next()) 752 assert.Nil(t, iter.Err()) 753 }) 754 } 755 } 756 757 var stockSnapshotsTestData = []string{ 758 `{ 759 "market_status": "late_trading", 760 "name": "Apple Inc.", 761 "session": { 762 "change": -0.07, 763 "change_percent": -0.0403, 764 "close": 173.5, 765 "early_trading_change": 0, 766 "early_trading_change_percent": 0, 767 "high": 173.85, 768 "low": 172.11, 769 "open": 172.48, 770 "previous_close": 173.57, 771 "price": 173.5, 772 "volume": 50823329 773 }, 774 "last_quote": { 775 "ask": 173.34, 776 "ask_size": 3, 777 "bid": 173.32, 778 "bid_size": 4, 779 "last_updated": 1683577209434314800, 780 "timeframe": "REAL-TIME" 781 }, 782 "last_trade": { 783 "conditions": [ 784 12, 785 22 786 ], 787 "exchange": 4, 788 "id": "247862", 789 "last_updated": 1683577205678289200, 790 "price": 173.5, 791 "size": 31535, 792 "timeframe": "REAL-TIME" 793 }, 794 "ticker": "AAPL", 795 "type": "stocks" 796 }`, 797 `{ 798 "market_status": "late_trading", 799 "name": "Meta Platforms, Inc. Class A Common Stock", 800 "session": { 801 "change": -0.04, 802 "change_percent": -0.0172, 803 "close": 233.27, 804 "early_trading_change": 0, 805 "early_trading_change_percent": 0, 806 "high": 235.62, 807 "low": 230.27, 808 "open": 231.415, 809 "previous_close": 232.78, 810 "price": 232.74, 811 "volume": 14940329 812 }, 813 "last_quote": { 814 "ask": 232.83, 815 "ask_size": 1, 816 "bid": 232.73, 817 "bid_size": 1, 818 "last_updated": 1683577187244746200, 819 "timeframe": "REAL-TIME" 820 }, 821 "last_trade": { 822 "conditions": [ 823 12, 824 37 825 ], 826 "exchange": 4, 827 "id": "57128", 828 "last_updated": 1683577202547284000, 829 "price": 232.74, 830 "size": 50, 831 "timeframe": "REAL-TIME" 832 }, 833 "ticker": "META", 834 "type": "stocks" 835 }`, 836 `{ 837 "market_status": "late_trading", 838 "name": "Ford Motor Company", 839 "session": { 840 "change": 0.005, 841 "change_percent": 0.0417, 842 "close": 12.02, 843 "early_trading_change": 0, 844 "early_trading_change_percent": 0, 845 "high": 12.055, 846 "low": 11.85, 847 "open": 12.02, 848 "previous_close": 11.99, 849 "price": 11.995, 850 "volume": 49539926 851 }, 852 "last_quote": { 853 "ask": 12, 854 "ask_size": 23, 855 "bid": 11.99, 856 "bid_size": 28, 857 "last_updated": 1683577084319878700, 858 "timeframe": "REAL-TIME" 859 }, 860 "last_trade": { 861 "conditions": [ 862 12, 863 37 864 ], 865 "exchange": 4, 866 "id": "71697320268354", 867 "last_updated": 1683577186411804000, 868 "price": 11.995, 869 "size": 1, 870 "timeframe": "REAL-TIME" 871 }, 872 "ticker": "F", 873 "type": "stocks" 874 }`, 875 } 876 877 var optionsSnapshotsTestData = []string{ 878 `{ 879 "name": "AAPL $50.00 call", 880 "market_status": "open", 881 "ticker": "O:AAPL230512C00050000", 882 "type": "options", 883 "last_quote": { 884 "ask": 123.1, 885 "ask_size": 90, 886 "bid": 122.95, 887 "bid_size": 90, 888 "last_updated": 1683731850932649728, 889 "midpoint": 123.025, 890 "timeframe": "REAL-TIME" 891 }, 892 "last_trade": {}, 893 "session": {}, 894 "break_even_price": 173.025, 895 "details": { 896 "contract_type": "call", 897 "exercise_style": "american", 898 "expiration_date": "2023-05-12", 899 "shares_per_contract": 100, 900 "strike_price": 50 901 }, 902 "greeks": {}, 903 "underlying_asset": { 904 "change_to_break_even": -0.11, 905 "last_updated": 1683732072879546553, 906 "price": 173.135, 907 "ticker": "AAPL", 908 "timeframe": "REAL-TIME" 909 }, 910 "fmv": 123.1, 911 "error": "", 912 "message": "" 913 }`, 914 `{ 915 "name": "META $20.00 call", 916 "market_status": "open", 917 "ticker": "O:META230512C00020000", 918 "type": "options", 919 "last_quote": {}, 920 "last_trade": { 921 "sip_timestamp": 1682970890371000000, 922 "conditions": [ 923 209 924 ], 925 "price": 223.75, 926 "size": 1, 927 "exchange": 302, 928 "timeframe": "REAL-TIME" 929 }, 930 "session": {}, 931 "details": { 932 "contract_type": "call", 933 "exercise_style": "american", 934 "expiration_date": "2023-05-12", 935 "shares_per_contract": 100, 936 "strike_price": 20 937 }, 938 "greeks": {}, 939 "underlying_asset": { 940 "last_updated": 1683731579449632715, 941 "price": 232.37, 942 "ticker": "META", 943 "timeframe": "REAL-TIME" 944 }, 945 "fmv": 200.2, 946 "error": "", 947 "message": "" 948 }`, 949 `{ 950 "name": "F $5.00 call", 951 "market_status": "open", 952 "ticker": "O:F230512C00005000", 953 "type": "options", 954 "last_quote": {}, 955 "last_trade": { 956 "sip_timestamp": 1683316735432000000, 957 "conditions": [ 958 232 959 ], 960 "price": 6.97, 961 "size": 1, 962 "exchange": 312, 963 "timeframe": "REAL-TIME" 964 }, 965 "session": {}, 966 "details": { 967 "contract_type": "call", 968 "exercise_style": "american", 969 "expiration_date": "2023-05-12", 970 "shares_per_contract": 100, 971 "strike_price": 5 972 }, 973 "greeks": {}, 974 "underlying_asset": { 975 "last_updated": 1683732072773028096, 976 "price": 11.93, 977 "ticker": "F", 978 "timeframe": "REAL-TIME" 979 }, 980 "fmv": 6.97 981 }`, 982 } 983 984 var cryptoSnapshotsTestData = []string{ 985 `{ 986 "market_status": "open", 987 "name": "Bitcoin - United States Dollar", 988 "ticker": "X:BTCUSD", 989 "type": "crypto", 990 "session": { 991 "change": -181, 992 "change_percent": -0.661, 993 "close": 27236.1, 994 "high": 27506, 995 "low": 27010, 996 "open": 27402.3, 997 "volume": 10012.03414028, 998 "previous_close": 27400.74, 999 "price": 27220 1000 }, 1001 "last_trade": { 1002 "participant_timestamp": 1684422449502000000, 1003 "timeframe": "REAL-TIME", 1004 "id": "285449387", 1005 "price": 27220, 1006 "exchange": 6, 1007 "conditions": [ 1008 1 1009 ] 1010 }, 1011 "fmv": 27220.3 1012 }`, 1013 `{ 1014 "market_status": "open", 1015 "name": "Ethereum - United States Dollar", 1016 "ticker": "X:ETHUSD", 1017 "type": "crypto", 1018 "session": { 1019 "change": -5.53, 1020 "change_percent": -0.304, 1021 "close": 1817.14, 1022 "high": 1833.4, 1023 "low": 1802.26, 1024 "open": 1823.8, 1025 "volume": 47673.72258305, 1026 "previous_close": 1821.84, 1027 "price": 1816.31 1028 }, 1029 "last_trade": { 1030 "participant_timestamp": 1684422449301037000, 1031 "timeframe": "REAL-TIME", 1032 "id": "451453400", 1033 "price": 1816.31, 1034 "exchange": 1, 1035 "conditions": [ 1036 1 1037 ] 1038 }, 1039 "fmv": 1816.31 1040 }`, 1041 `{ 1042 "market_status": "open", 1043 "name": "Flow - United States Dollar", 1044 "ticker": "X:FLOWUSD", 1045 "type": "crypto", 1046 "session": { 1047 "change": -0.006, 1048 "change_percent": -0.759, 1049 "close": 0.784, 1050 "high": 0.793, 1051 "low": 0.779, 1052 "open": 0.791, 1053 "volume": 89094.52551417, 1054 "previous_close": 0.79, 1055 "price": 0.784 1056 }, 1057 "last_trade": { 1058 "participant_timestamp": 1684422355917759000, 1059 "timeframe": "REAL-TIME", 1060 "id": "1224329", 1061 "price": 0.784, 1062 "exchange": 1, 1063 "conditions": [ 1064 1 1065 ] 1066 }, 1067 "fmv": 0.784 1068 }`, 1069 } 1070 1071 var forexSnapshotTestData = []string{ 1072 `{ 1073 "market_status": "open", 1074 "name": "United States dollar - Canadian dollar", 1075 "ticker": "C:USDCAD", 1076 "type": "fx", 1077 "session": { 1078 "change": 0.00518, 1079 "change_percent": 0.385, 1080 "close": 1.35178, 1081 "high": 1.35244, 1082 "low": 1.34479, 1083 "open": 1.34667, 1084 "volume": 133712, 1085 "previous_close": 1.34667 1086 }, 1087 "last_quote": { 1088 "last_updated": 1684431002000000000, 1089 "timeframe": "REAL-TIME", 1090 "ask": 1.35191, 1091 "bid": 1.35185, 1092 "exchange": 48 1093 }, 1094 "fmv": 1.35185 1095 }`, 1096 `{ 1097 "market_status": "open", 1098 "name": "United States dollar - Euro", 1099 "ticker": "C:USDEUR", 1100 "type": "fx", 1101 "session": { 1102 "change": 0.00612, 1103 "change_percent": 0.663, 1104 "close": 0.92877, 1105 "high": 0.92913, 1106 "low": 0.9215, 1107 "open": 0.92266, 1108 "volume": 58275, 1109 "previous_close": 0.92265 1110 }, 1111 "last_quote": { 1112 "last_updated": 1684430998000000000, 1113 "timeframe": "REAL-TIME", 1114 "ask": 0.92883, 1115 "bid": 0.92877, 1116 "exchange": 48 1117 }, 1118 "fmv": 0.92877 1119 }`, 1120 `{ 1121 "market_status": "open", 1122 "name": "United States dollar - Australian dollar", 1123 "ticker": "C:USDAUD", 1124 "type": "fx", 1125 "session": { 1126 "change": 0.0104, 1127 "change_percent": 0.692, 1128 "close": 1.512722, 1129 "high": 1.51398, 1130 "low": 1.4965803, 1131 "open": 1.502449, 1132 "volume": 148378, 1133 "previous_close": 1.502449 1134 }, 1135 "last_quote": { 1136 "last_updated": 1684431002000000000, 1137 "timeframe": "REAL-TIME", 1138 "ask": 1.51297, 1139 "bid": 1.51281, 1140 "exchange": 48 1141 }, 1142 "fmv": 1.51281 1143 }`, 1144 } 1145 1146 var indicesSnapshotTestData = []string{ 1147 `{ 1148 "value": 4191.9800000000005, 1149 "last_updated": 1684530171934000000, 1150 "timeframe": "REAL-TIME", 1151 "name": "Standard & Poor's 500", 1152 "ticker": "I:SPX", 1153 "market_status": "open", 1154 "type": "indices", 1155 "session": { 1156 "change": -9.09, 1157 "change_percent": -0.216, 1158 "close": 4180.7, 1159 "high": 4212.91, 1160 "low": 4180.2, 1161 "open": 4204.15, 1162 "previous_close": 4201.07 1163 } 1164 }`, 1165 `{ 1166 "value": 33426.63, 1167 "last_updated": 1684530172103533800, 1168 "timeframe": "REAL-TIME", 1169 "name": "Dow Jones Industrial Average", 1170 "ticker": "I:DJI", 1171 "market_status": "closed", 1172 "type": "indices", 1173 "session": { 1174 "change": -143, 1175 "change_percent": -0.426, 1176 "close": 33339.37, 1177 "high": 33652.9, 1178 "low": 33336.66, 1179 "open": 33582.95, 1180 "previous_close": 33569.5 1181 } 1182 }`, 1183 `{ 1184 "value": 443.7, 1185 "last_updated": 1684528800037231600, 1186 "timeframe": "REAL-TIME", 1187 "name": "Dow Jones Americas Basic Materials Index", 1188 "ticker": "I:A1BSC", 1189 "market_status": "closed", 1190 "type": "indices", 1191 "session": { 1192 "change": -0.29, 1193 "change_percent": -0.0653, 1194 "close": 442.72, 1195 "high": 445.92, 1196 "low": 442.63, 1197 "open": 443.7, 1198 "previous_close": 443.99 1199 } 1200 }`, 1201 } 1202 1203 var partialSuccessWithStocksTestData = []string{ 1204 `{ 1205 "market_status": "late_trading", 1206 "name": "Apple Inc.", 1207 "session": { 1208 "change": -0.07, 1209 "change_percent": -0.0403, 1210 "close": 173.5, 1211 "early_trading_change": 0, 1212 "early_trading_change_percent": 0, 1213 "high": 173.85, 1214 "low": 172.11, 1215 "open": 172.48, 1216 "previous_close": 173.57, 1217 "price": 173.5, 1218 "volume": 50823329 1219 }, 1220 "last_quote": { 1221 "ask": 173.34, 1222 "ask_size": 3, 1223 "bid": 173.32, 1224 "bid_size": 4, 1225 "last_updated": 1683577209434314800, 1226 "timeframe": "REAL-TIME" 1227 }, 1228 "last_trade": { 1229 "conditions": [ 1230 12, 1231 22 1232 ], 1233 "exchange": 4, 1234 "id": "247862", 1235 "last_updated": 1683577205678289200, 1236 "price": 173.5, 1237 "size": 31535, 1238 "timeframe": "REAL-TIME" 1239 }, 1240 "ticker": "AAPL", 1241 "type": "stocks", 1242 "fmv": 173.5 1243 }`, 1244 `{ 1245 "error": "NOT_ENTITLED", 1246 "message": "Not entitled to this ticker.", 1247 "ticker": "APy" 1248 }`, 1249 }