Skip to content

Reference

This part of the documentation focuses on an information-oriented approach. Use it as a reference for the technical implementation of the Theta Data Python API.

Finding a concept confusing? Let us know!

thetadata.client

Module that contains Theta Client class.

Contract

Contract

Source code in thetadata/client.py
Python
class Contract:
    """Contract"""
    def __init__(self):
        """Dummy constructor"""
        self.root = ""
        self.exp = None
        self.strike = None
        self.isCall = False
        self.isOption = False

    def from_bytes(self, data: bytes):
        """Deserializes a contract."""
        view = memoryview(data)
        parse_int = lambda d: int.from_bytes(d, "big")
        # parse
        len = parse_int(view[:1])
        root_len = parse_int(view[1:2])
        self.root = data[2:2 + root_len].decode("ascii")

        opt = parse_int(data[root_len + 2: root_len + 3])
        self.isOption = opt == 1
        if not self.isOption:
            return
        date_raw = str(parse_int(view[root_len + 3: root_len + 7]))
        self.exp = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))
        self.isCall = parse_int(view[root_len + 7: root_len + 8]) == 1
        self.strike = parse_int(view[root_len + 9: root_len + 13]) / 1000.0

    def to_string(self) -> str:
        """String representation of open interest."""
        return 'root: ' + self.root + ' isOption: ' + str(self.isOption) + ' exp: ' + str(self.exp) + \
               ' strike: ' + str(self.strike) + ' isCall: ' + str(self.isCall)

__init__()

Dummy constructor

Source code in thetadata/client.py
Python
def __init__(self):
    """Dummy constructor"""
    self.root = ""
    self.exp = None
    self.strike = None
    self.isCall = False
    self.isOption = False

from_bytes(data)

Deserializes a contract.

Source code in thetadata/client.py
Python
def from_bytes(self, data: bytes):
    """Deserializes a contract."""
    view = memoryview(data)
    parse_int = lambda d: int.from_bytes(d, "big")
    # parse
    len = parse_int(view[:1])
    root_len = parse_int(view[1:2])
    self.root = data[2:2 + root_len].decode("ascii")

    opt = parse_int(data[root_len + 2: root_len + 3])
    self.isOption = opt == 1
    if not self.isOption:
        return
    date_raw = str(parse_int(view[root_len + 3: root_len + 7]))
    self.exp = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))
    self.isCall = parse_int(view[root_len + 7: root_len + 8]) == 1
    self.strike = parse_int(view[root_len + 9: root_len + 13]) / 1000.0

to_string()

String representation of open interest.

Source code in thetadata/client.py
Python
def to_string(self) -> str:
    """String representation of open interest."""
    return 'root: ' + self.root + ' isOption: ' + str(self.isOption) + ' exp: ' + str(self.exp) + \
           ' strike: ' + str(self.strike) + ' isCall: ' + str(self.isCall)

OHLCVC

Trade representing all values provided by the Thetadata stream.

Source code in thetadata/client.py
Python
class OHLCVC:
    """Trade representing all values provided by the Thetadata stream."""
    def __init__(self):
        """Dummy constructor"""
        self.ms_of_day = 0
        self.open = 0
        self.high = 0
        self.low = 0
        self.close = 0
        self.volume = 0
        self.count = 0
        self.date = None

    def from_bytes(self, data: bytearray):
        """Deserializes a trade."""
        view = memoryview(data)
        parse_int = lambda d: int.from_bytes(d, "big")
        self.ms_of_day = parse_int(view[0:4])
        self.open   = round(parse_int(view[4:8]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
        self.high   = round(parse_int(view[8:12]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
        self.low    = round(parse_int(view[12:16]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
        self.close  = round(parse_int(view[16:20]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
        self.volume = parse_int(view[20:24])
        self.count  = parse_int(view[24:28])
        date_raw = str(parse_int(view[32:36]))
        self.date = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

    def copy_from(self, other_ohlcvc):
        self.ms_of_day = other_ohlcvc.ms_of_day
        self.open = other_ohlcvc.open
        self.high = other_ohlcvc.high
        self.low = other_ohlcvc.low
        self.close = other_ohlcvc.close
        self.volume = other_ohlcvc.volume
        self.count = other_ohlcvc.count
        self.date = other_ohlcvc.date

    def to_string(self) -> str:
        """String representation of a trade."""
        return 'ms_of_day: ' + str(self.ms_of_day) + ' open: ' + str(self.open) + ' high: ' + str(self.high) + \
               ' low: ' + str(self.low) + ' close: ' + str(self.close) + ' volume: ' + str(self.volume) +\
               ' count: ' + str(self.count) + ' date: ' + str(self.date)

__init__()

Dummy constructor

Source code in thetadata/client.py
Python
def __init__(self):
    """Dummy constructor"""
    self.ms_of_day = 0
    self.open = 0
    self.high = 0
    self.low = 0
    self.close = 0
    self.volume = 0
    self.count = 0
    self.date = None

from_bytes(data)

Deserializes a trade.

Source code in thetadata/client.py
Python
def from_bytes(self, data: bytearray):
    """Deserializes a trade."""
    view = memoryview(data)
    parse_int = lambda d: int.from_bytes(d, "big")
    self.ms_of_day = parse_int(view[0:4])
    self.open   = round(parse_int(view[4:8]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
    self.high   = round(parse_int(view[8:12]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
    self.low    = round(parse_int(view[12:16]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
    self.close  = round(parse_int(view[16:20]) * _pt_to_price_mul[parse_int(view[28:32])], 4)
    self.volume = parse_int(view[20:24])
    self.count  = parse_int(view[24:28])
    date_raw = str(parse_int(view[32:36]))
    self.date = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

to_string()

String representation of a trade.

Source code in thetadata/client.py
Python
def to_string(self) -> str:
    """String representation of a trade."""
    return 'ms_of_day: ' + str(self.ms_of_day) + ' open: ' + str(self.open) + ' high: ' + str(self.high) + \
           ' low: ' + str(self.low) + ' close: ' + str(self.close) + ' volume: ' + str(self.volume) +\
           ' count: ' + str(self.count) + ' date: ' + str(self.date)

OpenInterest

Open Interest

Source code in thetadata/client.py
Python
class OpenInterest:
    """Open Interest"""
    def __init__(self):
        """Dummy constructor"""
        self.open_interest = 0
        self.date = None

    def from_bytes(self, data: bytearray):
        """Deserializes open interest."""
        view = memoryview(data)
        parse_int = lambda d: int.from_bytes(d, "big")
        self.open_interest = parse_int(view[0:4])
        date_raw = str(parse_int(view[4:8]))
        self.date = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

    def copy_from(self, other_open_interest):
        self.open_interest = other_open_interest.open_interest
        self.date = other_open_interest.date

    def to_string(self) -> str:
        """String representation of open interest."""
        return 'open_interest: ' + str(self.open_interest) + ' date: ' + str(self.date)

__init__()

Dummy constructor

Source code in thetadata/client.py
Python
def __init__(self):
    """Dummy constructor"""
    self.open_interest = 0
    self.date = None

from_bytes(data)

Deserializes open interest.

Source code in thetadata/client.py
Python
def from_bytes(self, data: bytearray):
    """Deserializes open interest."""
    view = memoryview(data)
    parse_int = lambda d: int.from_bytes(d, "big")
    self.open_interest = parse_int(view[0:4])
    date_raw = str(parse_int(view[4:8]))
    self.date = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

to_string()

String representation of open interest.

Source code in thetadata/client.py
Python
def to_string(self) -> str:
    """String representation of open interest."""
    return 'open_interest: ' + str(self.open_interest) + ' date: ' + str(self.date)

Quote

Quote representing all values provided by the Thetadata stream.

Source code in thetadata/client.py
Python
class Quote:
    """Quote representing all values provided by the Thetadata stream."""
    def __init__(self):
        """Dummy constructor"""
        self.ms_of_day = 0
        self.bid_size = 0
        self.bid_exchange = Exchange.OPRA
        self.bid_price = 0
        self.bid_condition = QuoteCondition.UNDEFINED
        self.ask_size = 0
        self.ask_exchange = Exchange.OPRA
        self.ask_price = 0
        self.ask_condition = QuoteCondition.UNDEFINED
        self.date = None

    def from_bytes(self, data: bytes):
        """Deserializes a trade."""
        view = memoryview(data)
        parse_int = lambda d: int.from_bytes(d, "big")
        mult = _pt_to_price_mul[parse_int(view[36:40])]
        self.ms_of_day     = parse_int(view[0:4])
        self.bid_size      = parse_int(view[4:8])
        self.bid_exchange  = Exchange.from_code(parse_int(view[8:12]))
        self.bid_price     = round(parse_int(view[12:16]) * mult, 4)
        self.bid_condition = QuoteCondition.from_code(parse_int(view[16:20]))
        self.ask_size      = parse_int(view[20:24])
        self.ask_exchange  = Exchange.from_code(parse_int(view[24:28]))
        self.ask_price     = round(parse_int(view[28:32]) * mult, 4)
        self.ask_condition = QuoteCondition.from_code(parse_int(view[32:36]))
        date_raw           = str(parse_int(view[40:44]))
        self.date          = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

    def copy_from(self, other_quote):
        self.ms_of_day = other_quote.ms_of_day
        self.bid_size = other_quote.bid_size
        self.bid_exchange = other_quote.bid_exchange
        self.bid_price = other_quote.bid_price
        self.bid_condition = other_quote.bid_condition
        self.ask_size = other_quote.ask_size
        self.ask_exchange = other_quote.ask_exchange
        self.ask_price = other_quote.ask_price
        self.ask_condition = other_quote.ask_condition
        self.date = other_quote.date

    def to_string(self) -> str:
        """String representation of a quote."""
        return 'ms_of_day: ' + str(self.ms_of_day) + ' bid_size: ' + str(self.bid_size) + ' bid_exchange: ' + \
               str(self.bid_exchange.value[1]) + ' bid_price: ' + str(self.bid_price) + ' bid_condition: ' + \
               str(self.bid_condition.name) + ' ask_size: ' + str(self.ask_size) + ' ask_exchange: ' +\
               str(self.ask_exchange.value[1]) + ' ask_price: ' + str(self.ask_price) + ' ask_condition: ' \
               + str(self.ask_condition.name) + ' date: ' + str(self.date)

__init__()

Dummy constructor

Source code in thetadata/client.py
Python
def __init__(self):
    """Dummy constructor"""
    self.ms_of_day = 0
    self.bid_size = 0
    self.bid_exchange = Exchange.OPRA
    self.bid_price = 0
    self.bid_condition = QuoteCondition.UNDEFINED
    self.ask_size = 0
    self.ask_exchange = Exchange.OPRA
    self.ask_price = 0
    self.ask_condition = QuoteCondition.UNDEFINED
    self.date = None

from_bytes(data)

Deserializes a trade.

Source code in thetadata/client.py
Python
def from_bytes(self, data: bytes):
    """Deserializes a trade."""
    view = memoryview(data)
    parse_int = lambda d: int.from_bytes(d, "big")
    mult = _pt_to_price_mul[parse_int(view[36:40])]
    self.ms_of_day     = parse_int(view[0:4])
    self.bid_size      = parse_int(view[4:8])
    self.bid_exchange  = Exchange.from_code(parse_int(view[8:12]))
    self.bid_price     = round(parse_int(view[12:16]) * mult, 4)
    self.bid_condition = QuoteCondition.from_code(parse_int(view[16:20]))
    self.ask_size      = parse_int(view[20:24])
    self.ask_exchange  = Exchange.from_code(parse_int(view[24:28]))
    self.ask_price     = round(parse_int(view[28:32]) * mult, 4)
    self.ask_condition = QuoteCondition.from_code(parse_int(view[32:36]))
    date_raw           = str(parse_int(view[40:44]))
    self.date          = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

to_string()

String representation of a quote.

Source code in thetadata/client.py
Python
def to_string(self) -> str:
    """String representation of a quote."""
    return 'ms_of_day: ' + str(self.ms_of_day) + ' bid_size: ' + str(self.bid_size) + ' bid_exchange: ' + \
           str(self.bid_exchange.value[1]) + ' bid_price: ' + str(self.bid_price) + ' bid_condition: ' + \
           str(self.bid_condition.name) + ' ask_size: ' + str(self.ask_size) + ' ask_exchange: ' +\
           str(self.ask_exchange.value[1]) + ' ask_price: ' + str(self.ask_price) + ' ask_condition: ' \
           + str(self.ask_condition.name) + ' date: ' + str(self.date)

StreamMsg

Stream Msg

Source code in thetadata/client.py
Python
class StreamMsg:
    """Stream Msg"""
    def __init__(self):
        self.client = None
        self.type = StreamMsgType.ERROR
        self.req_response = None
        self.req_response_id = None
        self.trade = Trade()
        self.ohlcvc = OHLCVC()
        self.quote = Quote()
        self.open_interest = OpenInterest()
        self.contract = Contract()
        self.date = None

ThetaClient

A high-level, blocking client used to fetch market data. Instantiating this class runs a java background process, which is responsible for the heavy lifting of market data communication. Java 11 or higher is required to use this class.

Source code in thetadata/client.py
Python
 286
 287
 288
 289
 290
 291
 292
 293
 294
 295
 296
 297
 298
 299
 300
 301
 302
 303
 304
 305
 306
 307
 308
 309
 310
 311
 312
 313
 314
 315
 316
 317
 318
 319
 320
 321
 322
 323
 324
 325
 326
 327
 328
 329
 330
 331
 332
 333
 334
 335
 336
 337
 338
 339
 340
 341
 342
 343
 344
 345
 346
 347
 348
 349
 350
 351
 352
 353
 354
 355
 356
 357
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
class ThetaClient:
    """A high-level, blocking client used to fetch market data. Instantiating this class
    runs a java background process, which is responsible for the heavy lifting of market
    data communication. Java 11 or higher is required to use this class."""

    def __init__(self, port: int = 11000, timeout: Optional[float] = 60, launch: bool = True, jvm_mem: int = 0,
                 username: str = "default", passwd: str = "default", auto_update: bool = True, use_bundle: bool = True,
                 host: str = "127.0.0.1", streaming_port: int = 10000, stable: bool = True):
        """Construct a client instance to interface with market data. If no username and passwd fields are provided,
            the terminal will connect to thetadata servers with free data permissions.

        :param port: The port number specified in the Theta Terminal config, which can usually be found under
                        %user.home%/ThetaData/ThetaTerminal.
        :param streaming_port: The port number of Theta Terminal Stream server
        :param host: The host name or IP address of Theta Terminal server
        :param timeout: The max number of seconds to wait for a response before throwing a TimeoutError
        :param launch: Launches the terminal if true; uses an existing external terminal instance if false.
        :param jvm_mem: Any integer provided above zero will force the terminal to allocate a maximum amount of memory in GB.
        :param username: Theta Data email. Can be omitted with passwd if using free data.
        :param passwd: Theta Data password. Can be omitted with username if using free data.
        :param auto_update: If true, this class will automatically download the latest terminal version each time
            this class is instantiated. If false, the terminal will use the current jar terminal file. If none exists,
            it will download the latest version.
        :param use_bundle: Will download / use open-jdk-19.0.1 if True and the operating system is windows.
        """
        self.host: str = host
        self.port: int = port
        self.streaming_port: int = streaming_port
        self.timeout = timeout
        self._server: Optional[socket.socket] = None  # None while disconnected
        self._stream_server: Optional[socket.socket] = None  # None while disconnected
        self.launch = launch
        self._stream_impl = None
        self._stream_responses = {}
        self._counter_lock = threading.Lock()
        self._stream_req_id = 0
        self._stream_connected = False

        print('If you require API support, feel free to join our discord server! http://discord.thetadata.us')
        if launch:
            terminal.kill_existing_terminal()
            if username == "default" or passwd == "default":
                print('------------------------------------------------------------------------------------------------')
                print("You are using the free version of Theta Data. You are currently limited to "
                      "20 requests / minute.\nA data subscription can be purchased at https://thetadata.net. "
                      "If you already have a ThetaData\nsubscription, specify the username and passwd parameters.")
                print('------------------------------------------------------------------------------------------------')
            if check_download(auto_update, stable):
                Thread(target=launch_terminal, args=[username, passwd, use_bundle, jvm_mem, auto_update]).start()
        else:
            print("You are not launching the terminal. This means you should have an external instance already running.")

    @contextmanager
    def connect(self):
        """Initiate a connection with the Theta Terminal. Requests can only be made inside this
            generator aka the `with client.connect()` block.

        :raises ConnectionRefusedError: If the connection failed.
        :raises TimeoutError: If the timeout is set and has been reached.
        """

        try:
            for i in range(15):
                try:
                    self._server = socket.socket()
                    self._server.connect((self.host, self.port))
                    self._server.settimeout(1)
                    break
                except ConnectionError:
                    if i == 14:
                        raise ConnectionError('Unable to connect to the local Theta Terminal process.'
                                              ' Try restarting your system.')
                    sleep(1)
            self._server.settimeout(self.timeout)
            self._send_ver()
            yield
        finally:
            self._server.close()

    def connect_stream(self, callback) -> Thread:
        """Initiate a connection with the Theta Terminal Stream server.
        Requests can only be made inside this generator aka the `with client.connect_stream()` block.
        Responses to the provided callback method are recycled, meaning that if you send data received
        in the callback method to another thread, you must create a copy of it first.

        :raises ConnectionRefusedError: If the connection failed.
        :raises TimeoutError: If the timeout is set and has been reached.
        :return: The thread that is responsible for receiving messages.
        """
        for i in range(15):
            try:
                self._stream_server = socket.socket()
                self._stream_server.connect((self.host, self.streaming_port))
                self._stream_server.settimeout(1)
                break
            except ConnectionError:
                if i == 14:
                    raise ConnectionError('Unable to connect to the local Theta Terminal Stream process. '
                                          'Try restarting your system.')
                sleep(1)
        self._stream_server.settimeout(10)
        self._stream_impl = callback
        self._stream_connected = True
        out = Thread(target=self._recv_stream)
        out.start()
        return out

    def close_stream(self):
        self._stream_server.close()

    def req_full_trade_stream_opt(self) -> int:
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG

        with self._counter_lock:
            req_id = self._stream_req_id
            self._stream_responses[req_id] = None
            self._stream_req_id += 1

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={req_id}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))
        return req_id

    def req_full_open_interest_stream(self) -> id:
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG

        with self._counter_lock:
            req_id = self._stream_req_id
            self._stream_responses[req_id] = None
            self._stream_req_id += 1

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&sec={SecType.OPTION.value}&req={OptionReqType.OPEN_INTEREST.value}&id={req_id}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))
        return req_id

    def req_trade_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C') -> int:
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG
        # format data
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)

        with self._counter_lock:
            req_id = self._stream_req_id
            self._stream_responses[req_id] = None
            self._stream_req_id += 1

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={req_id}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))
        return req_id

    def req_quote_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C') -> int:
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG
        # format data
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)

        with self._counter_lock:
            req_id = self._stream_req_id
            self._stream_responses[req_id] = None
            self._stream_req_id += 1

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.QUOTE.value}&id={req_id}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))
        return req_id

    def remove_full_trade_stream_opt(self) -> int:
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG

        with self._counter_lock:
            req_id = self._stream_req_id
            self._stream_responses[req_id] = None
            self._stream_req_id += 1

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={req_id}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))
        return req_id

    def remove_full_open_interest_stream(self) -> id:
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG

        with self._counter_lock:
            req_id = self._stream_req_id
            self._stream_responses[req_id] = None
            self._stream_req_id += 1

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&sec={SecType.OPTION.value}&req={OptionReqType.OPEN_INTEREST.value}&id={req_id}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))
        return req_id

    def remove_trade_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C'):
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG
        # format data
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={-1}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))

    def remove_quote_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C'):
        """from_bytes
          """
        assert self._stream_server is not None, _NOT_CONNECTED_MSG
        # format data
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)

        with self._counter_lock:
            req_id = self._stream_req_id
            self._stream_responses[req_id] = None
            self._stream_req_id += 1

        # send request
        hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.QUOTE.value}&id={-1}\n"
        self._stream_server.sendall(hist_msg.encode("utf-8"))
        return req_id

    def verify(self, req_id: int, timeout: int = 5) -> StreamResponseType:
        tries = 0
        lim = timeout * 100
        while self._stream_responses[req_id] is None:  # This is kind of dumb.
            sleep(.01)
            tries += 1
            if tries >= lim:
                return StreamResponseType.TIMED_OUT

        return self._stream_responses[req_id]

    def _recv_stream(self):
        """from_bytes
          """
        msg = StreamMsg()
        msg.client = self
        parse_int = lambda d: int.from_bytes(d, "big")
        self._stream_server.settimeout(10)
        while self._stream_connected:
            try:
                msg.type = StreamMsgType.from_code(parse_int(self._read_stream(1)[:1]))
                msg.contract.from_bytes(self._read_stream(parse_int(self._read_stream(1)[:1])))
                if msg.type == StreamMsgType.QUOTE:
                    msg.quote.from_bytes(self._read_stream(44))
                elif msg.type == StreamMsgType.TRADE:
                    data = self._read_stream(n_bytes=32)
                    msg.trade.from_bytes(data)
                elif msg.type == StreamMsgType.OHLCVC:
                    data = self._read_stream(n_bytes=36)
                    msg.ohlcvc.from_bytes(data)
                elif msg.type == StreamMsgType.PING:
                    self._read_stream(n_bytes=4)
                elif msg.type == StreamMsgType.OPEN_INTEREST:
                    data = self._read_stream(n_bytes=8)
                    msg.open_interest.from_bytes(data)
                elif msg.type == StreamMsgType.REQ_RESPONSE:
                    msg.req_response_id = parse_int(self._read_stream(4))
                    msg.req_response = StreamResponseType.from_code(parse_int(self._read_stream(4)))
                    self._stream_responses[msg.req_response_id] = msg.req_response
                elif msg.type == StreamMsgType.STOP or msg.type == StreamMsgType.START:
                    msg.date = datetime.strptime(str(parse_int(self._read_stream(4))), "%Y%m%d").date()
                elif msg.type == StreamMsgType.DISCONNECTED or msg.type == StreamMsgType.RECONNECTED:
                    self._read_stream(4)  # Future use.
                else:
                    raise ValueError('undefined msg type: ' + str(msg.type))
            except (ConnectionResetError, OSError) as e:
                msg.type = StreamMsgType.STREAM_DEAD
                self._stream_impl(msg)
                self._stream_connected = False
                return
            except Exception as e:
                msg.type = StreamMsgType.ERROR
                print('Stream error for contract: ' + msg.contract.to_string())
                traceback.print_exc()
            self._stream_impl(msg)

    def _read_stream(self, n_bytes: int) -> bytearray:
        """from_bytes
          """

        buffer = bytearray(self._stream_server.recv(n_bytes))
        total = buffer.__len__()

        while total < n_bytes:
            part = self._stream_server.recv(n_bytes - total)
            if part.__len__() < 0:
                continue
            total += part.__len__()
            buffer.extend(part)
        return buffer

    def _send_ver(self):
        """Sends this API version to the Theta Terminal."""
        ver_msg = f"MSG_CODE={MessageType.HIST.value}&version={_VERSION}\n"
        self._server.sendall(ver_msg.encode("utf-8"))

    def _recv(self, n_bytes: int, progress_bar: bool = False) -> bytearray:
        """Wait for a response from the Terminal.
        :param n_bytes:       The number of bytes to receive.
        :param progress_bar:  Print a progress bar displaying download progress.
        :return:              A response from the Terminal.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG

        # receive body data in parts
        MAX_PART_SIZE = 256  # 4 KiB recommended for most machines

        buffer = bytearray(n_bytes)
        bytes_downloaded = 0

        # tqdm disable=True is slow bc it still calls __new__, which takes nearly 4ms
        range_ = range(0, n_bytes, MAX_PART_SIZE)
        iterable = tqdm(range_, desc="Downloading") if progress_bar else range_

        start = 0
        for i in iterable:
            part_size = min(MAX_PART_SIZE, n_bytes - bytes_downloaded)
            bytes_downloaded += part_size
            part = self._server.recv(part_size)
            if part.__len__() < 0:
                continue
            start += 1
            buffer[i: i + part_size] = part

        assert bytes_downloaded == n_bytes
        return buffer

    def kill(self, ignore_err=True) -> None:
        """Remotely kill the Terminal process. All subsequent requests will time out after this. A new instance of this
           class must be created.
        """
        if not ignore_err:
            assert self._server is not None, _NOT_CONNECTED_MSG

        kill_msg = f"MSG_CODE={MessageType.KILL.value}\n"
        try:
            self._server.sendall(kill_msg.encode("utf-8"))
        except OSError:
            if ignore_err:
                pass
            else:
                raise OSError

    # HIST DATA

    def get_hist_option(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight,
        date_range: DateRange,
        interval_size: int = 0,
        use_rth: bool = True,
        progress_bar: bool = False,
    ) -> pd.DataFrame:
        """
         Get historical options data.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.
        :param strike:         The strike price in USD, rounded to 1/10th of a cent.
        :param right:          The right of an option. CALL = Bullish; PUT = Bearish
        :param date_range:     The dates to fetch.
        :param interval_size:  The interval size in milliseconds. Applicable to most requests except ReqType.TRADE.
        :param use_rth:        If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored
                                  (only applicable to intervals requests).
        :param progress_bar:   Print a progress bar displaying download progress.

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        # format data
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)

        # send request
        hist_msg = f"MSG_CODE={MessageType.HIST.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}&rth={use_rth}&IVL={interval_size}\n"
        self._server.sendall(hist_msg.encode("utf-8"))

        # parse response header
        header_data = self._server.recv(20)
        header: Header = Header.parse(hist_msg, header_data)

        # parse response body
        body_data = self._recv(header.size, progress_bar=progress_bar)
        body: DataFrame = TickBody.parse(hist_msg, header, body_data)
        return body

    def get_hist_option_REST(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight,
        date_range: DateRange,
        interval_size: int = 0,
        use_rth: bool = True,
        host: str = "127.0.0.1",
        port: str = "25510"
    ) -> pd.DataFrame:
        """
         Get historical options data.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.
        :param strike:         The strike price in USD, rounded to 1/10th of a cent.
        :param right:          The right of an option. CALL = Bullish; PUT = Bearish
        :param date_range:     The dates to fetch.
        :param interval_size:  The interval size in milliseconds. Applicable to most requests except ReqType.TRADE.
        :param use_rth:        If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored
                                  (only applicable to intervals requests).
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        req_fmt = req.name.lower()
        strike_fmt = _format_strike(strike)
        exp_fmt = _format_date(exp)
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)
        right_fmt = right.value
        use_rth_fmt = str(use_rth).lower()
        url = f"http://{host}:{port}/hist/option/{req_fmt}"
        querystring = {"root": root, "start_date": start_fmt, "end_date": end_fmt,
                       "strike": strike_fmt, "exp": exp_fmt, "right": right_fmt,
                       "ivl": interval_size, "rth": use_rth_fmt}
        t1 = time.time()
        response = requests.get(url, params=querystring)
        t2 = time.time()
        df = parse_flexible_REST(response)
        t3 = time.time()

        return df

    def get_opt_at_time(
            self,
            req: OptionReqType,
            root: str,
            exp: date,
            strike: float,
            right: OptionRight,
            date_range: DateRange,
            ms_of_day: int = 0,
    ) -> pd.DataFrame:
        """
         Returns the last tick at a provided millisecond of the day for a given request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.
        :param strike:         The strike price in USD, rounded to 1/10th of a cent.
        :param right:          The right of an option. CALL = Bullish; PUT = Bearish
        :param date_range:     The dates to fetch.
        :param ms_of_day:      The time of day in milliseconds.

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        # format data
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)

        # send request
        hist_msg = f"MSG_CODE={MessageType.AT_TIME.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}&IVL={ms_of_day}\n"
        self._server.sendall(hist_msg.encode("utf-8"))

        # parse response header
        header_data = self._server.recv(20)
        header: Header = Header.parse(hist_msg, header_data)

        # parse response body
        body_data = self._recv(header.size, progress_bar=False)
        body: DataFrame = TickBody.parse(hist_msg, header, body_data)
        return body

    def get_opt_at_time_REST(
            self,
            req: OptionReqType,
            root: str,
            exp: date,
            strike: float,
            right: OptionRight,
            date_range: DateRange,
            ms_of_day: int = 0,
            host: str = "127.0.0.1",
            port: str = "25510"
    ) -> pd.DataFrame:
        """
         Returns the last tick at a provided millisecond of the day for a given request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.
        :param strike:         The strike price in USD, rounded to 1/10th of a cent.
        :param right:          The right of an option. CALL = Bullish; PUT = Bearish
        :param date_range:     The dates to fetch.
        :param ms_of_day:      The time of day in milliseconds.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        # format data
        req_fmt = req.name.lower()
        strike_fmt = _format_strike(strike)
        exp_fmt = _format_date(exp)
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)
        right_fmt = right.value

        url = f"http://{host}:{port}/at_time/option/{req_fmt}"
        querystring = {"root": root, "start_date": start_fmt, "end_date": end_fmt, "strike": strike_fmt,
                       "exp": exp_fmt, "right": right_fmt, "ivl": ms_of_day}
        response = requests.get(url, params=querystring)
        df = parse_flexible_REST(response)
        return df

    def get_stk_at_time(
            self,
            req: StockReqType,
            root: str,
            date_range: DateRange,
            ms_of_day: int = 0,
    ) -> pd.DataFrame:
        """
         Returns the last tick at a provided millisecond of the day for a given request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param date_range:     The dates to fetch.
        :param ms_of_day:      The time of day in milliseconds.

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        # format data
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)

        # send request
        hist_msg = f"MSG_CODE={MessageType.AT_TIME.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&sec={SecType.STOCK.value}&req={req.value}&IVL={ms_of_day}\n"
        self._server.sendall(hist_msg.encode("utf-8"))

        # parse response header
        header_data = self._server.recv(20)
        header: Header = Header.parse(hist_msg, header_data)

        # parse response body
        body_data = self._recv(header.size, progress_bar=False)
        body: DataFrame = TickBody.parse(hist_msg, header, body_data)
        return body

    def get_stk_at_time_REST(
            self,
            req: StockReqType,
            root: str,
            date_range: DateRange,
            ms_of_day: int = 0,
            host: str = "127.0.0.1",
            port: str = "25510"
    ) -> pd.DataFrame:
        """
         Returns the last tick at a provided millisecond of the day for a given request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param date_range:     The dates to fetch.
        :param ms_of_day:      The time of day in milliseconds.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        req_fmt = req.name.lower()
        root_fmt = root.lower()
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)

        url = f"http://{host}:{port}/at_time/stock/{req_fmt}"
        querystring = {"root": root_fmt, "start_date": start_fmt,
                       "end_date": end_fmt, "ivl": ms_of_day}
        response = requests.get(url, params=querystring)
        df = parse_flexible_REST(response)
        return df

    def get_hist_stock(
            self,
            req: StockReqType,
            root: str,
            date_range: DateRange,
            interval_size: int = 0,
            use_rth: bool = True,
            progress_bar: bool = False,
    ) -> pd.DataFrame:
        """
         Get historical stock data.

        :param req:            The request type.
        :param root:           The root symbol.
        :param date_range:     The dates to fetch.
        :param interval_size:  The interval size in milliseconds. Applicable only to OHLC & QUOTE requests.
        :param use_rth:         If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored.
        :param progress_bar:   Print a progress bar displaying download progress.

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        # format data
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)

        # send request
        hist_msg = f"MSG_CODE={MessageType.HIST.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&sec={SecType.STOCK.value}&req={req.value}&rth={use_rth}&IVL={interval_size}\n"
        self._server.sendall(hist_msg.encode("utf-8"))

        # parse response header
        header_data = self._server.recv(20)
        header: Header = Header.parse(hist_msg, header_data)

        # parse response body
        body_data = self._recv(header.size, progress_bar=progress_bar)
        body: DataFrame = TickBody.parse(hist_msg, header, body_data)
        return body

    def get_hist_stock_REST(
            self,
            req: StockReqType,
            root: str,
            date_range: DateRange,
            interval_size: int = 0,
            use_rth: bool = True,
            host: str = "127.0.0.1",
            port: str = "25510"
    ) -> pd.DataFrame:
        """
         Get historical stock data.

        :param req:            The request type.
        :param root:           The root symbol.
        :param date_range:     The dates to fetch.
        :param interval_size:  The interval size in milliseconds. Applicable only to OHLC & QUOTE requests.
        :param use_rth:         If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        # format data
        req_fmt = req.name.lower()
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)
        use_rth_fmt = str(use_rth).lower()
        url = f"http://{host}:{port}/hist/stock/{req_fmt}"
        params = {"root": root, "start_date": start_fmt, "end_date": end_fmt,
                      "ivl": interval_size, "rth": use_rth_fmt}
        response = requests.get(url, params=params)
        df = parse_flexible_REST(response)
        return df

    # LISTING DATA

    def get_dates_stk(self, root: str, req: StockReqType) -> pd.Series:
        """
        Get all dates of data available for a given stock contract and request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.

        :return:               All dates that Theta Data provides data for given a request.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        out = f"MSG_CODE={MessageType.ALL_DATES.value}&root={root}&sec={SecType.STOCK.value}&req={req.value}\n"
        self._server.send(out.encode("utf-8"))
        header = Header.parse(out, self._server.recv(20))
        body = ListBody.parse(out, header, self._recv(header.size), dates=True)
        return body.lst

    def get_dates_stk_REST(self, root: str, req: StockReqType, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
        """
        Get all dates of data available for a given stock contract and request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               dAll dates that Theta Data provides data for given a request.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        root_fmt = root.lower()
        req_fmt = req.name.lower()
        url = f"http://{host}:{port}/list/dates/stock/{req_fmt}"
        params = {'root': root_fmt}
        response = requests.get(url, params=params)
        series = parse_list_REST(response, dates=True)
        return series

    def get_dates_opt(
            self,
            req: OptionReqType,
            root: str,
            exp: date,
            strike: float,
            right: OptionRight) -> pd.Series:
        """
        Get all dates of data available for a given options contract and request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.
        :param strike:         The strike price in USD.
        :param right:          The right of an options.

        :return:               All dates that Theta Data provides data for given a request.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)
        out = f"MSG_CODE={MessageType.ALL_DATES.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}\n"
        self._server.send(out.encode("utf-8"))
        header = Header.parse(out, self._server.recv(20))
        body = ListBody.parse(out, header, self._recv(header.size), dates=True)
        return body.lst

    def get_dates_opt_REST(
            self,
            req: OptionReqType,
            root: str,
            exp: date,
            strike: float,
            right: OptionRight,
            host: str = "127.0.0.1",
            port: str = "25510") -> pd.Series:
        """
        Get all dates of data available for a given options contract and request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.
        :param strike:         The strike price in USD.
        :param right:          The right of an options.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               All dates that Theta Data provides data for given a request.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        req = req.name.lower()
        exp_fmt = _format_date(exp)
        strike_fmt = _format_strike(strike)
        right = right.value
        sec = SecType.OPTION.value.lower()
        url = f"http://{host}:{port}/list/dates/{sec}/{req}"
        params = {'root': root, 'exp': exp_fmt, 'strike': strike_fmt, 'right': right}
        response = requests.get(url, params=params)
        df = parse_list_REST(response, dates=True)
        return df

    def get_dates_opt_bulk(
            self,
            req: OptionReqType,
            root: str,
            exp: date) -> pd.Series:
        """
        Get all dates of data available for a given options expiration and request type.

        :param req:            The request type.
        :param root:           The root symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.

        :return:               All dates that Theta Data provides data for given options chain (expiration).
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        exp_fmt = _format_date(exp)
        out = f"MSG_CODE={MessageType.ALL_DATES_BULK.value}&root={root}&exp={exp_fmt}&sec={SecType.OPTION.value}&req={req.value}\n"
        self._server.send(out.encode("utf-8"))
        header = Header.parse(out, self._server.recv(20))
        body = ListBody.parse(out, header, self._recv(header.size), dates=True)
        return body.lst

    def get_dates_opt_bulk_REST(
            self,
            req: OptionReqType,
            root: str,
            exp: date,
            host: str = "127.0.0.1",
            port: str = "25510") -> pd.Series:
        """
        Get all dates of data available for a given options contract and request type.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date. Must be after the start of `date_range`.
        :param strike:         The strike price in USD.
        :param right:          The right of an options.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               All dates that Theta Data provides data for given a request.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        req = req.name.lower()
        exp_fmt = _format_date(exp)
        sec = SecType.OPTION.value.lower()
        url = f"http://{host}:{port}/list/dates/{sec}/{req}"
        params = {'root': root, 'exp': exp_fmt}
        response = requests.get(url, params=params)
        df = parse_list_REST(response, dates=True)
        return df

    def get_expirations(self, root: str) -> pd.Series:
        """
        Get all options expirations for a provided underlying root.

        :param root:           The root / underlying / ticker / symbol.

        :return:               All expirations that ThetaData provides data for.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        out = f"MSG_CODE={MessageType.ALL_EXPIRATIONS.value}&root={root}\n"
        self._server.send(out.encode("utf-8"))
        header = Header.parse(out, self._server.recv(20))
        body = ListBody.parse(out, header, self._recv(header.size), dates=True)
        return body.lst

    def get_expirations_REST(self, root: str, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
        """
        Get all options expirations for a provided underlying root.

        :param root:           The root / underlying / ticker / symbol.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               All expirations that ThetaData provides data for.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        url = f"http://{host}:{port}/list/expirations"
        params = {"root": root}
        response = requests.get(url, params=params)
        df = parse_list_REST(response, dates=True)
        return df

    def get_strikes(self, root: str, exp: date, date_range: DateRange = None,) -> pd.Series:
        """
        Get all options strike prices in US tenths of a cent.

        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date.
        :param date_range:     If specified, this function will return strikes only if they have data for every
                                day in the date range.

        :return:               The strike prices on the expiration.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        assert isinstance(exp, date)
        exp_fmt = _format_date(exp)

        if date_range is not None:
            start_fmt = _format_date(date_range.start)
            end_fmt = _format_date(date_range.end)
            out = f"MSG_CODE={MessageType.ALL_STRIKES.value}&root={root}&exp={exp_fmt}&START_DATE={start_fmt}&END_DATE={end_fmt}\n"
        else:
            out = f"MSG_CODE={MessageType.ALL_STRIKES.value}&root={root}&exp={exp_fmt}\n"
        self._server.send(out.encode("utf-8"))
        header = Header.parse(out, self._server.recv(20))
        body = ListBody.parse(out, header, self._recv(header.size)).lst
        div = Decimal(1000)
        s = pd.Series([], dtype='float64')
        c = 0
        for i in body:
            s[c] = Decimal(i) / div
            c += 1

        return s


    def get_strikes_REST(self, root: str, exp: date, date_range: DateRange = None, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
        """
        Get all options strike prices in US tenths of a cent.

        :param root:           The root / underlying / ticker / symbol.
        :param exp:            The expiration date.
        :param date_range:     If specified, this function will return strikes only if they have data for every
                                day in the date range.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               The strike prices on the expiration.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert isinstance(exp, date)
        exp_fmt = _format_date(exp)
        root_fmt = root.lower()
        if date_range is not None:
            start_fmt = _format_date(date_range.start)
            end_fmt = _format_date(date_range.end)
            querystring = {"root": root_fmt, "exp": exp_fmt}
        else:
            querystring = {"root": root_fmt, "exp": exp_fmt}
        url = f"http://{host}:{port}/list/strikes"
        response = requests.get(url, params=querystring)
        ser = parse_list_REST(response)
        ser = ser.divide(1000)
        return ser

    def get_roots(self, sec: SecType) -> pd.Series:
        """
        Get all roots for a certain security type.

        :param sec: The type of security.

        :return: All roots / underlyings / tickers / symbols for the security type.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        out = f"MSG_CODE={MessageType.ALL_ROOTS.value}&sec={sec.value}\n"
        self._server.send(out.encode("utf-8"))
        header = Header.parse(out, self._server.recv(20))
        body = ListBody.parse(out, header, self._recv(header.size))
        return body.lst

    def get_roots_REST(self, sec: SecType, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
        """
        Get all roots for a certain security type.

        :param sec: The type of security.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return: All roots / underlyings / tickers / symbols for the security type.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        url = f"http://{host}:{port}/list/roots"
        params = {'sec': sec.value}
        response = requests.get(url, params=params)
        df = parse_list_REST(response)
        return df

    # LIVE DATA

    def get_last_option(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight,
    ) -> pd.DataFrame:
        """
        Get the most recent options tick.

        :param req:            The request type.
        :param root:           The root symbol.
        :param exp:            The expiration date.
        :param strike:         The strike price in USD, rounded to 1/10th of a cent.
        :param right:          The right of an options.

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        # format data
        strike = _format_strike(strike)
        exp_fmt = _format_date(exp)

        # send request
        hist_msg = f"MSG_CODE={MessageType.LAST.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}\n"
        self._server.sendall(hist_msg.encode("utf-8"))

        # parse response
        header: Header = Header.parse(hist_msg, self._server.recv(20))
        body: DataFrame = TickBody.parse(
            hist_msg, header, self._recv(header.size)
        )
        return body

    def get_last_option_REST(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight,
        host: str = "127.0.0.1",
        port: str = "25510"
    ) -> pd.DataFrame:
        """
        Get the most recent options tick.

        :param req:            The request type.
        :param root:           The root symbol.
        :param exp:            The expiration date.
        :param strike:         The strike price in USD, rounded to 1/10th of a cent.
        :param right:          The right of an options.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        root_fmt = root.lower()
        req_fmt = req.name.lower()
        right_fmt = right.value
        strike_fmt = _format_strike(strike)
        exp_fmt = _format_date(exp)

        url = f"http://{host}:{port}/snapshot/option/{req_fmt}"
        querystring = {"root": root_fmt, "strike": strike_fmt, "exp": exp_fmt, "right": right_fmt}
        response = requests.get(url, params=querystring)
        df = parse_flexible_REST(response)
        return df

    def get_last_stock(
        self,
        req: StockReqType,
        root: str,
    ) -> pd.DataFrame:
        """
        Get the most recent stock tick.

        :param req:            The request type.
        :param root:           The root / underlying / ticker / symbol.

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG

        # send request
        hist_msg = f"MSG_CODE={MessageType.LAST.value}&root={root}&sec={SecType.STOCK.value}&req={req.value}\n"
        self._server.sendall(hist_msg.encode("utf-8"))

        # parse response
        header: Header = Header.parse(hist_msg, self._server.recv(20))
        body: DataFrame = TickBody.parse(
            hist_msg, header, self._recv(header.size)
        )
        return body

    def get_last_stock_REST(
        self,
        req: StockReqType,
        root: str,
        host: str = "127.0.0.1",
        port: str = "25510"
    ) -> pd.DataFrame:
        """
        Get the most recent options tick.

        :param req:            The request type.
        :param root:           The root symbol.
        :param exp:            The expiration date.
        :param strike:         The strike price in USD, rounded to 1/10th of a cent.
        :param right:          The right of an options.
        :param host:           The ip address of the server
        :param port:           The port of the server

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        root_fmt = root.lower()
        req_fmt = req.name.lower()

        url = f"http://{host}:{port}/snapshot/option/{req_fmt}"
        querystring = {"root": root_fmt}
        response = requests.get(url, params=querystring)
        df = parse_flexible_REST(response)
        return df

    def get_req(
        self,
        req: str,
    ) -> pd.DataFrame:
        """
        Make a historical data request given the raw text output of a data request. Typically used for debugging.

        :param req:            The raw request.

        :return:               The requested data as a pandas DataFrame.
        :raises ResponseError: If the request failed.
        :raises NoData:        If there is no data available for the request.
        """
        assert self._server is not None, _NOT_CONNECTED_MSG
        # send request
        req = req + "\n"
        self._server.sendall(req.encode("utf-8"))

        # parse response header
        header_data = self._server.recv(20)
        header: Header = Header.parse(req, header_data)

        # parse response body
        body_data = self._recv(header.size, progress_bar=False)
        body: DataFrame = TickBody.parse(req, header, body_data)
        return body

__init__(port=11000, timeout=60, launch=True, jvm_mem=0, username='default', passwd='default', auto_update=True, use_bundle=True, host='127.0.0.1', streaming_port=10000, stable=True)

Construct a client instance to interface with market data. If no username and passwd fields are provided, the terminal will connect to thetadata servers with free data permissions.

Parameters:

Name Type Description Default
port int

The port number specified in the Theta Terminal config, which can usually be found under %user.home%/ThetaData/ThetaTerminal.

11000
streaming_port int

The port number of Theta Terminal Stream server

10000
host str

The host name or IP address of Theta Terminal server

'127.0.0.1'
timeout Optional[float]

The max number of seconds to wait for a response before throwing a TimeoutError

60
launch bool

Launches the terminal if true; uses an existing external terminal instance if false.

True
jvm_mem int

Any integer provided above zero will force the terminal to allocate a maximum amount of memory in GB.

0
username str

Theta Data email. Can be omitted with passwd if using free data.

'default'
passwd str

Theta Data password. Can be omitted with username if using free data.

'default'
auto_update bool

If true, this class will automatically download the latest terminal version each time this class is instantiated. If false, the terminal will use the current jar terminal file. If none exists, it will download the latest version.

True
use_bundle bool

Will download / use open-jdk-19.0.1 if True and the operating system is windows.

True
Source code in thetadata/client.py
Python
def __init__(self, port: int = 11000, timeout: Optional[float] = 60, launch: bool = True, jvm_mem: int = 0,
             username: str = "default", passwd: str = "default", auto_update: bool = True, use_bundle: bool = True,
             host: str = "127.0.0.1", streaming_port: int = 10000, stable: bool = True):
    """Construct a client instance to interface with market data. If no username and passwd fields are provided,
        the terminal will connect to thetadata servers with free data permissions.

    :param port: The port number specified in the Theta Terminal config, which can usually be found under
                    %user.home%/ThetaData/ThetaTerminal.
    :param streaming_port: The port number of Theta Terminal Stream server
    :param host: The host name or IP address of Theta Terminal server
    :param timeout: The max number of seconds to wait for a response before throwing a TimeoutError
    :param launch: Launches the terminal if true; uses an existing external terminal instance if false.
    :param jvm_mem: Any integer provided above zero will force the terminal to allocate a maximum amount of memory in GB.
    :param username: Theta Data email. Can be omitted with passwd if using free data.
    :param passwd: Theta Data password. Can be omitted with username if using free data.
    :param auto_update: If true, this class will automatically download the latest terminal version each time
        this class is instantiated. If false, the terminal will use the current jar terminal file. If none exists,
        it will download the latest version.
    :param use_bundle: Will download / use open-jdk-19.0.1 if True and the operating system is windows.
    """
    self.host: str = host
    self.port: int = port
    self.streaming_port: int = streaming_port
    self.timeout = timeout
    self._server: Optional[socket.socket] = None  # None while disconnected
    self._stream_server: Optional[socket.socket] = None  # None while disconnected
    self.launch = launch
    self._stream_impl = None
    self._stream_responses = {}
    self._counter_lock = threading.Lock()
    self._stream_req_id = 0
    self._stream_connected = False

    print('If you require API support, feel free to join our discord server! http://discord.thetadata.us')
    if launch:
        terminal.kill_existing_terminal()
        if username == "default" or passwd == "default":
            print('------------------------------------------------------------------------------------------------')
            print("You are using the free version of Theta Data. You are currently limited to "
                  "20 requests / minute.\nA data subscription can be purchased at https://thetadata.net. "
                  "If you already have a ThetaData\nsubscription, specify the username and passwd parameters.")
            print('------------------------------------------------------------------------------------------------')
        if check_download(auto_update, stable):
            Thread(target=launch_terminal, args=[username, passwd, use_bundle, jvm_mem, auto_update]).start()
    else:
        print("You are not launching the terminal. This means you should have an external instance already running.")

connect()

Initiate a connection with the Theta Terminal. Requests can only be made inside this generator aka the with client.connect() block.

Raises:

Type Description
ConnectionRefusedError

If the connection failed.

TimeoutError

If the timeout is set and has been reached.

Source code in thetadata/client.py
Python
@contextmanager
def connect(self):
    """Initiate a connection with the Theta Terminal. Requests can only be made inside this
        generator aka the `with client.connect()` block.

    :raises ConnectionRefusedError: If the connection failed.
    :raises TimeoutError: If the timeout is set and has been reached.
    """

    try:
        for i in range(15):
            try:
                self._server = socket.socket()
                self._server.connect((self.host, self.port))
                self._server.settimeout(1)
                break
            except ConnectionError:
                if i == 14:
                    raise ConnectionError('Unable to connect to the local Theta Terminal process.'
                                          ' Try restarting your system.')
                sleep(1)
        self._server.settimeout(self.timeout)
        self._send_ver()
        yield
    finally:
        self._server.close()

connect_stream(callback)

Initiate a connection with the Theta Terminal Stream server. Requests can only be made inside this generator aka the with client.connect_stream() block. Responses to the provided callback method are recycled, meaning that if you send data received in the callback method to another thread, you must create a copy of it first.

Returns:

Type Description
Thread

The thread that is responsible for receiving messages.

Raises:

Type Description
ConnectionRefusedError

If the connection failed.

TimeoutError

If the timeout is set and has been reached.

Source code in thetadata/client.py
Python
def connect_stream(self, callback) -> Thread:
    """Initiate a connection with the Theta Terminal Stream server.
    Requests can only be made inside this generator aka the `with client.connect_stream()` block.
    Responses to the provided callback method are recycled, meaning that if you send data received
    in the callback method to another thread, you must create a copy of it first.

    :raises ConnectionRefusedError: If the connection failed.
    :raises TimeoutError: If the timeout is set and has been reached.
    :return: The thread that is responsible for receiving messages.
    """
    for i in range(15):
        try:
            self._stream_server = socket.socket()
            self._stream_server.connect((self.host, self.streaming_port))
            self._stream_server.settimeout(1)
            break
        except ConnectionError:
            if i == 14:
                raise ConnectionError('Unable to connect to the local Theta Terminal Stream process. '
                                      'Try restarting your system.')
            sleep(1)
    self._stream_server.settimeout(10)
    self._stream_impl = callback
    self._stream_connected = True
    out = Thread(target=self._recv_stream)
    out.start()
    return out

get_dates_opt(req, root, exp, strike, right)

Get all dates of data available for a given options contract and request type.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required
strike float

The strike price in USD.

required
right OptionRight

The right of an options.

required

Returns:

Type Description
Series

All dates that Theta Data provides data for given a request.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_dates_opt(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight) -> pd.Series:
    """
    Get all dates of data available for a given options contract and request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.
    :param strike:         The strike price in USD.
    :param right:          The right of an options.

    :return:               All dates that Theta Data provides data for given a request.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)
    out = f"MSG_CODE={MessageType.ALL_DATES.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}\n"
    self._server.send(out.encode("utf-8"))
    header = Header.parse(out, self._server.recv(20))
    body = ListBody.parse(out, header, self._recv(header.size), dates=True)
    return body.lst

get_dates_opt_REST(req, root, exp, strike, right, host='127.0.0.1', port='25510')

Get all dates of data available for a given options contract and request type.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required
strike float

The strike price in USD.

required
right OptionRight

The right of an options.

required
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
Series

All dates that Theta Data provides data for given a request.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_dates_opt_REST(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight,
        host: str = "127.0.0.1",
        port: str = "25510") -> pd.Series:
    """
    Get all dates of data available for a given options contract and request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.
    :param strike:         The strike price in USD.
    :param right:          The right of an options.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               All dates that Theta Data provides data for given a request.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    req = req.name.lower()
    exp_fmt = _format_date(exp)
    strike_fmt = _format_strike(strike)
    right = right.value
    sec = SecType.OPTION.value.lower()
    url = f"http://{host}:{port}/list/dates/{sec}/{req}"
    params = {'root': root, 'exp': exp_fmt, 'strike': strike_fmt, 'right': right}
    response = requests.get(url, params=params)
    df = parse_list_REST(response, dates=True)
    return df

get_dates_opt_bulk(req, root, exp)

Get all dates of data available for a given options expiration and request type.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required

Returns:

Type Description
Series

All dates that Theta Data provides data for given options chain (expiration).

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_dates_opt_bulk(
        self,
        req: OptionReqType,
        root: str,
        exp: date) -> pd.Series:
    """
    Get all dates of data available for a given options expiration and request type.

    :param req:            The request type.
    :param root:           The root symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.

    :return:               All dates that Theta Data provides data for given options chain (expiration).
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    exp_fmt = _format_date(exp)
    out = f"MSG_CODE={MessageType.ALL_DATES_BULK.value}&root={root}&exp={exp_fmt}&sec={SecType.OPTION.value}&req={req.value}\n"
    self._server.send(out.encode("utf-8"))
    header = Header.parse(out, self._server.recv(20))
    body = ListBody.parse(out, header, self._recv(header.size), dates=True)
    return body.lst

get_dates_opt_bulk_REST(req, root, exp, host='127.0.0.1', port='25510')

Get all dates of data available for a given options contract and request type.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required
strike

The strike price in USD.

required
right

The right of an options.

required
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
Series

All dates that Theta Data provides data for given a request.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_dates_opt_bulk_REST(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        host: str = "127.0.0.1",
        port: str = "25510") -> pd.Series:
    """
    Get all dates of data available for a given options contract and request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.
    :param strike:         The strike price in USD.
    :param right:          The right of an options.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               All dates that Theta Data provides data for given a request.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    req = req.name.lower()
    exp_fmt = _format_date(exp)
    sec = SecType.OPTION.value.lower()
    url = f"http://{host}:{port}/list/dates/{sec}/{req}"
    params = {'root': root, 'exp': exp_fmt}
    response = requests.get(url, params=params)
    df = parse_list_REST(response, dates=True)
    return df

get_dates_stk(root, req)

Get all dates of data available for a given stock contract and request type.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required

Returns:

Type Description
Series

All dates that Theta Data provides data for given a request.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_dates_stk(self, root: str, req: StockReqType) -> pd.Series:
    """
    Get all dates of data available for a given stock contract and request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.

    :return:               All dates that Theta Data provides data for given a request.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    out = f"MSG_CODE={MessageType.ALL_DATES.value}&root={root}&sec={SecType.STOCK.value}&req={req.value}\n"
    self._server.send(out.encode("utf-8"))
    header = Header.parse(out, self._server.recv(20))
    body = ListBody.parse(out, header, self._recv(header.size), dates=True)
    return body.lst

get_dates_stk_REST(root, req, host='127.0.0.1', port='25510')

Get all dates of data available for a given stock contract and request type.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
Series

dAll dates that Theta Data provides data for given a request.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_dates_stk_REST(self, root: str, req: StockReqType, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
    """
    Get all dates of data available for a given stock contract and request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               dAll dates that Theta Data provides data for given a request.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    root_fmt = root.lower()
    req_fmt = req.name.lower()
    url = f"http://{host}:{port}/list/dates/stock/{req_fmt}"
    params = {'root': root_fmt}
    response = requests.get(url, params=params)
    series = parse_list_REST(response, dates=True)
    return series

get_expirations(root)

Get all options expirations for a provided underlying root.

Parameters:

Name Type Description Default
root str

The root / underlying / ticker / symbol.

required

Returns:

Type Description
Series

All expirations that ThetaData provides data for.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_expirations(self, root: str) -> pd.Series:
    """
    Get all options expirations for a provided underlying root.

    :param root:           The root / underlying / ticker / symbol.

    :return:               All expirations that ThetaData provides data for.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    out = f"MSG_CODE={MessageType.ALL_EXPIRATIONS.value}&root={root}\n"
    self._server.send(out.encode("utf-8"))
    header = Header.parse(out, self._server.recv(20))
    body = ListBody.parse(out, header, self._recv(header.size), dates=True)
    return body.lst

get_expirations_REST(root, host='127.0.0.1', port='25510')

Get all options expirations for a provided underlying root.

Parameters:

Name Type Description Default
root str

The root / underlying / ticker / symbol.

required
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
Series

All expirations that ThetaData provides data for.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_expirations_REST(self, root: str, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
    """
    Get all options expirations for a provided underlying root.

    :param root:           The root / underlying / ticker / symbol.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               All expirations that ThetaData provides data for.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    url = f"http://{host}:{port}/list/expirations"
    params = {"root": root}
    response = requests.get(url, params=params)
    df = parse_list_REST(response, dates=True)
    return df

get_hist_option(req, root, exp, strike, right, date_range, interval_size=0, use_rth=True, progress_bar=False)

Get historical options data.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required
strike float

The strike price in USD, rounded to 1/10th of a cent.

required
right OptionRight

The right of an option. CALL = Bullish; PUT = Bearish

required
date_range DateRange

The dates to fetch.

required
interval_size int

The interval size in milliseconds. Applicable to most requests except ReqType.TRADE.

0
use_rth bool

If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored (only applicable to intervals requests).

True
progress_bar bool

Print a progress bar displaying download progress.

False

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_hist_option(
    self,
    req: OptionReqType,
    root: str,
    exp: date,
    strike: float,
    right: OptionRight,
    date_range: DateRange,
    interval_size: int = 0,
    use_rth: bool = True,
    progress_bar: bool = False,
) -> pd.DataFrame:
    """
     Get historical options data.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.
    :param strike:         The strike price in USD, rounded to 1/10th of a cent.
    :param right:          The right of an option. CALL = Bullish; PUT = Bearish
    :param date_range:     The dates to fetch.
    :param interval_size:  The interval size in milliseconds. Applicable to most requests except ReqType.TRADE.
    :param use_rth:        If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored
                              (only applicable to intervals requests).
    :param progress_bar:   Print a progress bar displaying download progress.

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    # format data
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)

    # send request
    hist_msg = f"MSG_CODE={MessageType.HIST.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}&rth={use_rth}&IVL={interval_size}\n"
    self._server.sendall(hist_msg.encode("utf-8"))

    # parse response header
    header_data = self._server.recv(20)
    header: Header = Header.parse(hist_msg, header_data)

    # parse response body
    body_data = self._recv(header.size, progress_bar=progress_bar)
    body: DataFrame = TickBody.parse(hist_msg, header, body_data)
    return body

get_hist_option_REST(req, root, exp, strike, right, date_range, interval_size=0, use_rth=True, host='127.0.0.1', port='25510')

Get historical options data.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required
strike float

The strike price in USD, rounded to 1/10th of a cent.

required
right OptionRight

The right of an option. CALL = Bullish; PUT = Bearish

required
date_range DateRange

The dates to fetch.

required
interval_size int

The interval size in milliseconds. Applicable to most requests except ReqType.TRADE.

0
use_rth bool

If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored (only applicable to intervals requests).

True
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_hist_option_REST(
    self,
    req: OptionReqType,
    root: str,
    exp: date,
    strike: float,
    right: OptionRight,
    date_range: DateRange,
    interval_size: int = 0,
    use_rth: bool = True,
    host: str = "127.0.0.1",
    port: str = "25510"
) -> pd.DataFrame:
    """
     Get historical options data.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.
    :param strike:         The strike price in USD, rounded to 1/10th of a cent.
    :param right:          The right of an option. CALL = Bullish; PUT = Bearish
    :param date_range:     The dates to fetch.
    :param interval_size:  The interval size in milliseconds. Applicable to most requests except ReqType.TRADE.
    :param use_rth:        If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored
                              (only applicable to intervals requests).
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    req_fmt = req.name.lower()
    strike_fmt = _format_strike(strike)
    exp_fmt = _format_date(exp)
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)
    right_fmt = right.value
    use_rth_fmt = str(use_rth).lower()
    url = f"http://{host}:{port}/hist/option/{req_fmt}"
    querystring = {"root": root, "start_date": start_fmt, "end_date": end_fmt,
                   "strike": strike_fmt, "exp": exp_fmt, "right": right_fmt,
                   "ivl": interval_size, "rth": use_rth_fmt}
    t1 = time.time()
    response = requests.get(url, params=querystring)
    t2 = time.time()
    df = parse_flexible_REST(response)
    t3 = time.time()

    return df

get_hist_stock(req, root, date_range, interval_size=0, use_rth=True, progress_bar=False)

Get historical stock data.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root symbol.

required
date_range DateRange

The dates to fetch.

required
interval_size int

The interval size in milliseconds. Applicable only to OHLC & QUOTE requests.

0
use_rth bool

If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored.

True
progress_bar bool

Print a progress bar displaying download progress.

False

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_hist_stock(
        self,
        req: StockReqType,
        root: str,
        date_range: DateRange,
        interval_size: int = 0,
        use_rth: bool = True,
        progress_bar: bool = False,
) -> pd.DataFrame:
    """
     Get historical stock data.

    :param req:            The request type.
    :param root:           The root symbol.
    :param date_range:     The dates to fetch.
    :param interval_size:  The interval size in milliseconds. Applicable only to OHLC & QUOTE requests.
    :param use_rth:         If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored.
    :param progress_bar:   Print a progress bar displaying download progress.

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    # format data
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)

    # send request
    hist_msg = f"MSG_CODE={MessageType.HIST.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&sec={SecType.STOCK.value}&req={req.value}&rth={use_rth}&IVL={interval_size}\n"
    self._server.sendall(hist_msg.encode("utf-8"))

    # parse response header
    header_data = self._server.recv(20)
    header: Header = Header.parse(hist_msg, header_data)

    # parse response body
    body_data = self._recv(header.size, progress_bar=progress_bar)
    body: DataFrame = TickBody.parse(hist_msg, header, body_data)
    return body

get_hist_stock_REST(req, root, date_range, interval_size=0, use_rth=True, host='127.0.0.1', port='25510')

Get historical stock data.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root symbol.

required
date_range DateRange

The dates to fetch.

required
interval_size int

The interval size in milliseconds. Applicable only to OHLC & QUOTE requests.

0
use_rth bool

If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored.

True
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_hist_stock_REST(
        self,
        req: StockReqType,
        root: str,
        date_range: DateRange,
        interval_size: int = 0,
        use_rth: bool = True,
        host: str = "127.0.0.1",
        port: str = "25510"
) -> pd.DataFrame:
    """
     Get historical stock data.

    :param req:            The request type.
    :param root:           The root symbol.
    :param date_range:     The dates to fetch.
    :param interval_size:  The interval size in milliseconds. Applicable only to OHLC & QUOTE requests.
    :param use_rth:         If true, timestamps prior to 09:30 EST and after 16:00 EST will be ignored.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    # format data
    req_fmt = req.name.lower()
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)
    use_rth_fmt = str(use_rth).lower()
    url = f"http://{host}:{port}/hist/stock/{req_fmt}"
    params = {"root": root, "start_date": start_fmt, "end_date": end_fmt,
                  "ivl": interval_size, "rth": use_rth_fmt}
    response = requests.get(url, params=params)
    df = parse_flexible_REST(response)
    return df

get_last_option(req, root, exp, strike, right)

Get the most recent options tick.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root symbol.

required
exp date

The expiration date.

required
strike float

The strike price in USD, rounded to 1/10th of a cent.

required
right OptionRight

The right of an options.

required

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_last_option(
    self,
    req: OptionReqType,
    root: str,
    exp: date,
    strike: float,
    right: OptionRight,
) -> pd.DataFrame:
    """
    Get the most recent options tick.

    :param req:            The request type.
    :param root:           The root symbol.
    :param exp:            The expiration date.
    :param strike:         The strike price in USD, rounded to 1/10th of a cent.
    :param right:          The right of an options.

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    # format data
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)

    # send request
    hist_msg = f"MSG_CODE={MessageType.LAST.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}\n"
    self._server.sendall(hist_msg.encode("utf-8"))

    # parse response
    header: Header = Header.parse(hist_msg, self._server.recv(20))
    body: DataFrame = TickBody.parse(
        hist_msg, header, self._recv(header.size)
    )
    return body

get_last_option_REST(req, root, exp, strike, right, host='127.0.0.1', port='25510')

Get the most recent options tick.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root symbol.

required
exp date

The expiration date.

required
strike float

The strike price in USD, rounded to 1/10th of a cent.

required
right OptionRight

The right of an options.

required
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_last_option_REST(
    self,
    req: OptionReqType,
    root: str,
    exp: date,
    strike: float,
    right: OptionRight,
    host: str = "127.0.0.1",
    port: str = "25510"
) -> pd.DataFrame:
    """
    Get the most recent options tick.

    :param req:            The request type.
    :param root:           The root symbol.
    :param exp:            The expiration date.
    :param strike:         The strike price in USD, rounded to 1/10th of a cent.
    :param right:          The right of an options.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    root_fmt = root.lower()
    req_fmt = req.name.lower()
    right_fmt = right.value
    strike_fmt = _format_strike(strike)
    exp_fmt = _format_date(exp)

    url = f"http://{host}:{port}/snapshot/option/{req_fmt}"
    querystring = {"root": root_fmt, "strike": strike_fmt, "exp": exp_fmt, "right": right_fmt}
    response = requests.get(url, params=querystring)
    df = parse_flexible_REST(response)
    return df

get_last_stock(req, root)

Get the most recent stock tick.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_last_stock(
    self,
    req: StockReqType,
    root: str,
) -> pd.DataFrame:
    """
    Get the most recent stock tick.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG

    # send request
    hist_msg = f"MSG_CODE={MessageType.LAST.value}&root={root}&sec={SecType.STOCK.value}&req={req.value}\n"
    self._server.sendall(hist_msg.encode("utf-8"))

    # parse response
    header: Header = Header.parse(hist_msg, self._server.recv(20))
    body: DataFrame = TickBody.parse(
        hist_msg, header, self._recv(header.size)
    )
    return body

get_last_stock_REST(req, root, host='127.0.0.1', port='25510')

Get the most recent options tick.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root symbol.

required
exp

The expiration date.

required
strike

The strike price in USD, rounded to 1/10th of a cent.

required
right

The right of an options.

required
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_last_stock_REST(
    self,
    req: StockReqType,
    root: str,
    host: str = "127.0.0.1",
    port: str = "25510"
) -> pd.DataFrame:
    """
    Get the most recent options tick.

    :param req:            The request type.
    :param root:           The root symbol.
    :param exp:            The expiration date.
    :param strike:         The strike price in USD, rounded to 1/10th of a cent.
    :param right:          The right of an options.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    root_fmt = root.lower()
    req_fmt = req.name.lower()

    url = f"http://{host}:{port}/snapshot/option/{req_fmt}"
    querystring = {"root": root_fmt}
    response = requests.get(url, params=querystring)
    df = parse_flexible_REST(response)
    return df

get_opt_at_time(req, root, exp, strike, right, date_range, ms_of_day=0)

Returns the last tick at a provided millisecond of the day for a given request type.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required
strike float

The strike price in USD, rounded to 1/10th of a cent.

required
right OptionRight

The right of an option. CALL = Bullish; PUT = Bearish

required
date_range DateRange

The dates to fetch.

required
ms_of_day int

The time of day in milliseconds.

0

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_opt_at_time(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight,
        date_range: DateRange,
        ms_of_day: int = 0,
) -> pd.DataFrame:
    """
     Returns the last tick at a provided millisecond of the day for a given request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.
    :param strike:         The strike price in USD, rounded to 1/10th of a cent.
    :param right:          The right of an option. CALL = Bullish; PUT = Bearish
    :param date_range:     The dates to fetch.
    :param ms_of_day:      The time of day in milliseconds.

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    # format data
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)

    # send request
    hist_msg = f"MSG_CODE={MessageType.AT_TIME.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={req.value}&IVL={ms_of_day}\n"
    self._server.sendall(hist_msg.encode("utf-8"))

    # parse response header
    header_data = self._server.recv(20)
    header: Header = Header.parse(hist_msg, header_data)

    # parse response body
    body_data = self._recv(header.size, progress_bar=False)
    body: DataFrame = TickBody.parse(hist_msg, header, body_data)
    return body

get_opt_at_time_REST(req, root, exp, strike, right, date_range, ms_of_day=0, host='127.0.0.1', port='25510')

Returns the last tick at a provided millisecond of the day for a given request type.

Parameters:

Name Type Description Default
req OptionReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date. Must be after the start of date_range.

required
strike float

The strike price in USD, rounded to 1/10th of a cent.

required
right OptionRight

The right of an option. CALL = Bullish; PUT = Bearish

required
date_range DateRange

The dates to fetch.

required
ms_of_day int

The time of day in milliseconds.

0
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_opt_at_time_REST(
        self,
        req: OptionReqType,
        root: str,
        exp: date,
        strike: float,
        right: OptionRight,
        date_range: DateRange,
        ms_of_day: int = 0,
        host: str = "127.0.0.1",
        port: str = "25510"
) -> pd.DataFrame:
    """
     Returns the last tick at a provided millisecond of the day for a given request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date. Must be after the start of `date_range`.
    :param strike:         The strike price in USD, rounded to 1/10th of a cent.
    :param right:          The right of an option. CALL = Bullish; PUT = Bearish
    :param date_range:     The dates to fetch.
    :param ms_of_day:      The time of day in milliseconds.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    # format data
    req_fmt = req.name.lower()
    strike_fmt = _format_strike(strike)
    exp_fmt = _format_date(exp)
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)
    right_fmt = right.value

    url = f"http://{host}:{port}/at_time/option/{req_fmt}"
    querystring = {"root": root, "start_date": start_fmt, "end_date": end_fmt, "strike": strike_fmt,
                   "exp": exp_fmt, "right": right_fmt, "ivl": ms_of_day}
    response = requests.get(url, params=querystring)
    df = parse_flexible_REST(response)
    return df

get_req(req)

Make a historical data request given the raw text output of a data request. Typically used for debugging.

Parameters:

Name Type Description Default
req str

The raw request.

required

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_req(
    self,
    req: str,
) -> pd.DataFrame:
    """
    Make a historical data request given the raw text output of a data request. Typically used for debugging.

    :param req:            The raw request.

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    # send request
    req = req + "\n"
    self._server.sendall(req.encode("utf-8"))

    # parse response header
    header_data = self._server.recv(20)
    header: Header = Header.parse(req, header_data)

    # parse response body
    body_data = self._recv(header.size, progress_bar=False)
    body: DataFrame = TickBody.parse(req, header, body_data)
    return body

get_roots(sec)

Get all roots for a certain security type.

Parameters:

Name Type Description Default
sec SecType

The type of security.

required

Returns:

Type Description
Series

All roots / underlyings / tickers / symbols for the security type.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_roots(self, sec: SecType) -> pd.Series:
    """
    Get all roots for a certain security type.

    :param sec: The type of security.

    :return: All roots / underlyings / tickers / symbols for the security type.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    out = f"MSG_CODE={MessageType.ALL_ROOTS.value}&sec={sec.value}\n"
    self._server.send(out.encode("utf-8"))
    header = Header.parse(out, self._server.recv(20))
    body = ListBody.parse(out, header, self._recv(header.size))
    return body.lst

get_roots_REST(sec, host='127.0.0.1', port='25510')

Get all roots for a certain security type.

Parameters:

Name Type Description Default
sec SecType

The type of security.

required
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
Series

All roots / underlyings / tickers / symbols for the security type.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_roots_REST(self, sec: SecType, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
    """
    Get all roots for a certain security type.

    :param sec: The type of security.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return: All roots / underlyings / tickers / symbols for the security type.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    url = f"http://{host}:{port}/list/roots"
    params = {'sec': sec.value}
    response = requests.get(url, params=params)
    df = parse_list_REST(response)
    return df

get_stk_at_time(req, root, date_range, ms_of_day=0)

Returns the last tick at a provided millisecond of the day for a given request type.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
date_range DateRange

The dates to fetch.

required
ms_of_day int

The time of day in milliseconds.

0

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_stk_at_time(
        self,
        req: StockReqType,
        root: str,
        date_range: DateRange,
        ms_of_day: int = 0,
) -> pd.DataFrame:
    """
     Returns the last tick at a provided millisecond of the day for a given request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param date_range:     The dates to fetch.
    :param ms_of_day:      The time of day in milliseconds.

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    # format data
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)

    # send request
    hist_msg = f"MSG_CODE={MessageType.AT_TIME.value}&START_DATE={start_fmt}&END_DATE={end_fmt}&root={root}&sec={SecType.STOCK.value}&req={req.value}&IVL={ms_of_day}\n"
    self._server.sendall(hist_msg.encode("utf-8"))

    # parse response header
    header_data = self._server.recv(20)
    header: Header = Header.parse(hist_msg, header_data)

    # parse response body
    body_data = self._recv(header.size, progress_bar=False)
    body: DataFrame = TickBody.parse(hist_msg, header, body_data)
    return body

get_stk_at_time_REST(req, root, date_range, ms_of_day=0, host='127.0.0.1', port='25510')

Returns the last tick at a provided millisecond of the day for a given request type.

Parameters:

Name Type Description Default
req StockReqType

The request type.

required
root str

The root / underlying / ticker / symbol.

required
date_range DateRange

The dates to fetch.

required
ms_of_day int

The time of day in milliseconds.

0
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
DataFrame

The requested data as a pandas DataFrame.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_stk_at_time_REST(
        self,
        req: StockReqType,
        root: str,
        date_range: DateRange,
        ms_of_day: int = 0,
        host: str = "127.0.0.1",
        port: str = "25510"
) -> pd.DataFrame:
    """
     Returns the last tick at a provided millisecond of the day for a given request type.

    :param req:            The request type.
    :param root:           The root / underlying / ticker / symbol.
    :param date_range:     The dates to fetch.
    :param ms_of_day:      The time of day in milliseconds.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               The requested data as a pandas DataFrame.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    req_fmt = req.name.lower()
    root_fmt = root.lower()
    start_fmt = _format_date(date_range.start)
    end_fmt = _format_date(date_range.end)

    url = f"http://{host}:{port}/at_time/stock/{req_fmt}"
    querystring = {"root": root_fmt, "start_date": start_fmt,
                   "end_date": end_fmt, "ivl": ms_of_day}
    response = requests.get(url, params=querystring)
    df = parse_flexible_REST(response)
    return df

get_strikes(root, exp, date_range=None)

Get all options strike prices in US tenths of a cent.

Parameters:

Name Type Description Default
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date.

required
date_range DateRange

If specified, this function will return strikes only if they have data for every day in the date range.

None

Returns:

Type Description
Series

The strike prices on the expiration.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_strikes(self, root: str, exp: date, date_range: DateRange = None,) -> pd.Series:
    """
    Get all options strike prices in US tenths of a cent.

    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date.
    :param date_range:     If specified, this function will return strikes only if they have data for every
                            day in the date range.

    :return:               The strike prices on the expiration.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert self._server is not None, _NOT_CONNECTED_MSG
    assert isinstance(exp, date)
    exp_fmt = _format_date(exp)

    if date_range is not None:
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)
        out = f"MSG_CODE={MessageType.ALL_STRIKES.value}&root={root}&exp={exp_fmt}&START_DATE={start_fmt}&END_DATE={end_fmt}\n"
    else:
        out = f"MSG_CODE={MessageType.ALL_STRIKES.value}&root={root}&exp={exp_fmt}\n"
    self._server.send(out.encode("utf-8"))
    header = Header.parse(out, self._server.recv(20))
    body = ListBody.parse(out, header, self._recv(header.size)).lst
    div = Decimal(1000)
    s = pd.Series([], dtype='float64')
    c = 0
    for i in body:
        s[c] = Decimal(i) / div
        c += 1

    return s

get_strikes_REST(root, exp, date_range=None, host='127.0.0.1', port='25510')

Get all options strike prices in US tenths of a cent.

Parameters:

Name Type Description Default
root str

The root / underlying / ticker / symbol.

required
exp date

The expiration date.

required
date_range DateRange

If specified, this function will return strikes only if they have data for every day in the date range.

None
host str

The ip address of the server

'127.0.0.1'
port str

The port of the server

'25510'

Returns:

Type Description
Series

The strike prices on the expiration.

Raises:

Type Description
ResponseError

If the request failed.

NoData

If there is no data available for the request.

Source code in thetadata/client.py
Python
def get_strikes_REST(self, root: str, exp: date, date_range: DateRange = None, host: str = "127.0.0.1", port: str = "25510") -> pd.Series:
    """
    Get all options strike prices in US tenths of a cent.

    :param root:           The root / underlying / ticker / symbol.
    :param exp:            The expiration date.
    :param date_range:     If specified, this function will return strikes only if they have data for every
                            day in the date range.
    :param host:           The ip address of the server
    :param port:           The port of the server

    :return:               The strike prices on the expiration.
    :raises ResponseError: If the request failed.
    :raises NoData:        If there is no data available for the request.
    """
    assert isinstance(exp, date)
    exp_fmt = _format_date(exp)
    root_fmt = root.lower()
    if date_range is not None:
        start_fmt = _format_date(date_range.start)
        end_fmt = _format_date(date_range.end)
        querystring = {"root": root_fmt, "exp": exp_fmt}
    else:
        querystring = {"root": root_fmt, "exp": exp_fmt}
    url = f"http://{host}:{port}/list/strikes"
    response = requests.get(url, params=querystring)
    ser = parse_list_REST(response)
    ser = ser.divide(1000)
    return ser

kill(ignore_err=True)

Remotely kill the Terminal process. All subsequent requests will time out after this. A new instance of this class must be created.

Source code in thetadata/client.py
Python
def kill(self, ignore_err=True) -> None:
    """Remotely kill the Terminal process. All subsequent requests will time out after this. A new instance of this
       class must be created.
    """
    if not ignore_err:
        assert self._server is not None, _NOT_CONNECTED_MSG

    kill_msg = f"MSG_CODE={MessageType.KILL.value}\n"
    try:
        self._server.sendall(kill_msg.encode("utf-8"))
    except OSError:
        if ignore_err:
            pass
        else:
            raise OSError

remove_full_open_interest_stream()

from_bytes

Source code in thetadata/client.py
Python
def remove_full_open_interest_stream(self) -> id:
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG

    with self._counter_lock:
        req_id = self._stream_req_id
        self._stream_responses[req_id] = None
        self._stream_req_id += 1

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&sec={SecType.OPTION.value}&req={OptionReqType.OPEN_INTEREST.value}&id={req_id}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))
    return req_id

remove_full_trade_stream_opt()

from_bytes

Source code in thetadata/client.py
Python
def remove_full_trade_stream_opt(self) -> int:
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG

    with self._counter_lock:
        req_id = self._stream_req_id
        self._stream_responses[req_id] = None
        self._stream_req_id += 1

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={req_id}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))
    return req_id

remove_quote_stream_opt(root, exp=0, strike=0, right='C')

from_bytes

Source code in thetadata/client.py
Python
def remove_quote_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C'):
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG
    # format data
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)

    with self._counter_lock:
        req_id = self._stream_req_id
        self._stream_responses[req_id] = None
        self._stream_req_id += 1

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.QUOTE.value}&id={-1}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))
    return req_id

remove_trade_stream_opt(root, exp=0, strike=0, right='C')

from_bytes

Source code in thetadata/client.py
Python
def remove_trade_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C'):
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG
    # format data
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REMOVE.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={-1}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))

req_full_open_interest_stream()

from_bytes

Source code in thetadata/client.py
Python
def req_full_open_interest_stream(self) -> id:
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG

    with self._counter_lock:
        req_id = self._stream_req_id
        self._stream_responses[req_id] = None
        self._stream_req_id += 1

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&sec={SecType.OPTION.value}&req={OptionReqType.OPEN_INTEREST.value}&id={req_id}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))
    return req_id

req_full_trade_stream_opt()

from_bytes

Source code in thetadata/client.py
Python
def req_full_trade_stream_opt(self) -> int:
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG

    with self._counter_lock:
        req_id = self._stream_req_id
        self._stream_responses[req_id] = None
        self._stream_req_id += 1

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={req_id}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))
    return req_id

req_quote_stream_opt(root, exp=0, strike=0, right='C')

from_bytes

Source code in thetadata/client.py
Python
def req_quote_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C') -> int:
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG
    # format data
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)

    with self._counter_lock:
        req_id = self._stream_req_id
        self._stream_responses[req_id] = None
        self._stream_req_id += 1

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.QUOTE.value}&id={req_id}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))
    return req_id

req_trade_stream_opt(root, exp=0, strike=0, right='C')

from_bytes

Source code in thetadata/client.py
Python
def req_trade_stream_opt(self, root: str, exp: date = 0, strike: float = 0, right: OptionRight = 'C') -> int:
    """from_bytes
      """
    assert self._stream_server is not None, _NOT_CONNECTED_MSG
    # format data
    strike = _format_strike(strike)
    exp_fmt = _format_date(exp)

    with self._counter_lock:
        req_id = self._stream_req_id
        self._stream_responses[req_id] = None
        self._stream_req_id += 1

    # send request
    hist_msg = f"MSG_CODE={MessageType.STREAM_REQ.value}&root={root}&exp={exp_fmt}&strike={strike}&right={right.value}&sec={SecType.OPTION.value}&req={OptionReqType.TRADE.value}&id={req_id}\n"
    self._stream_server.sendall(hist_msg.encode("utf-8"))
    return req_id

Trade

Trade representing all values provided by the Thetadata stream.

Source code in thetadata/client.py
Python
class Trade:
    """Trade representing all values provided by the Thetadata stream."""
    def __init__(self):
        """Dummy constructor"""
        self.ms_of_day = 0
        self.sequence = 0
        self.size = 0
        self.condition = TradeCondition.UNDEFINED
        self.price = 0
        self.exchange = None
        self.date = None

    def from_bytes(self, data: bytearray):
        """Deserializes a trade."""
        view = memoryview(data)
        parse_int = lambda d: int.from_bytes(d, "big")
        self.ms_of_day = parse_int(view[0:4])
        self.sequence = parse_int(view[4:8]) & 0xffffffffffffffff
        self.size = parse_int(view[8:12])
        self.condition = TradeCondition.from_code(parse_int(view[12:16]))
        self.price = round(parse_int(view[16:20]) * _pt_to_price_mul[parse_int(view[24:28])], 4)
        self.exchange = Exchange.from_code(parse_int(view[20:24]))
        date_raw = str(parse_int(view[28:32]))
        self.date = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

    def copy_from(self, other_trade):
        self.ms_of_day = other_trade.ms_of_day
        self.sequence = other_trade.sequence
        self.size = other_trade.size
        self.condition = other_trade.condition
        self.price = other_trade.price
        self.exchange = other_trade.exchange
        self.date = other_trade.date

    def to_string(self) -> str:
        """String representation of a trade."""
        return 'ms_of_day: ' + str(self.ms_of_day) + ' sequence: ' + str(self.sequence) + ' size: ' + str(self.size) + \
               ' condition: ' + str(self.condition.name) + ' price: ' + str(self.price) + ' exchange: ' + \
               str(self.exchange.value[1]) + ' date: ' + str(self.date)

__init__()

Dummy constructor

Source code in thetadata/client.py
Python
def __init__(self):
    """Dummy constructor"""
    self.ms_of_day = 0
    self.sequence = 0
    self.size = 0
    self.condition = TradeCondition.UNDEFINED
    self.price = 0
    self.exchange = None
    self.date = None

from_bytes(data)

Deserializes a trade.

Source code in thetadata/client.py
Python
def from_bytes(self, data: bytearray):
    """Deserializes a trade."""
    view = memoryview(data)
    parse_int = lambda d: int.from_bytes(d, "big")
    self.ms_of_day = parse_int(view[0:4])
    self.sequence = parse_int(view[4:8]) & 0xffffffffffffffff
    self.size = parse_int(view[8:12])
    self.condition = TradeCondition.from_code(parse_int(view[12:16]))
    self.price = round(parse_int(view[16:20]) * _pt_to_price_mul[parse_int(view[24:28])], 4)
    self.exchange = Exchange.from_code(parse_int(view[20:24]))
    date_raw = str(parse_int(view[28:32]))
    self.date = date(year=int(date_raw[0:4]), month=int(date_raw[4:6]), day=int(date_raw[6:8]))

to_string()

String representation of a trade.

Source code in thetadata/client.py
Python
def to_string(self) -> str:
    """String representation of a trade."""
    return 'ms_of_day: ' + str(self.ms_of_day) + ' sequence: ' + str(self.sequence) + ' size: ' + str(self.size) + \
           ' condition: ' + str(self.condition.name) + ' price: ' + str(self.price) + ' exchange: ' + \
           str(self.exchange.value[1]) + ' date: ' + str(self.date)

ms_to_time(ms_of_day)

Converts milliseconds of day to a time object.

Source code in thetadata/client.py
Python
def ms_to_time(ms_of_day: int) -> datetime.time:
    """Converts milliseconds of day to a time object."""
    return datetime(year=2000, month=1, day=1, hour=int((ms_of_day / (1000 * 60 * 60)) % 24),
                    minute=int(ms_of_day / (1000 * 60)) % 60, second=int((ms_of_day / 1000) % 60),
                    microsecond=(ms_of_day % 1000) * 1000).time()

thetadata.enums

Module that contains various enums necessary to interface w the Terminal.

DataType

Bases: Enum

Codes used in the format tick to ID the data in the body ticks.

Source code in thetadata/enums.py
Python
@enum.unique
class DataType(enum.Enum):
    """Codes used in the format tick to ID the data in the body ticks."""

    DATE = (0, False)
    MS_OF_DAY = (1, False)
    CORRECTION = (2, False)
    PRICE_TYPE = (4, False)
    MS_OF_DAY2 = (5, False)

    # QUOTES
    BID_SIZE = (101, False)
    BID_EXCHANGE = (102, False)
    BID = (103, True)
    BID_CONDITION = (104, False)
    ASK_SIZE = (105, False)
    ASK_EXCHANGE = (106, False)
    ASK = (107, True)
    ASK_CONDITION = (108, False)

    # PRICING
    MIDPOINT = (111, True)
    VWAP = (112, True)
    QWAP = (113, True)
    WAP = (114, True)

    # OPEN INTEREST
    OPEN_INTEREST = (121, True)

    # TRADES
    SEQUENCE = (131, False)
    SIZE = (132, False)
    CONDITION = (133, False)
    PRICE = (134, True)

    # VOLUME
    VOLUME = (141, False)
    COUNT = (142, False)

    # FIRST ORDER GREEKS
    THETA = (151, True)
    VEGA = (152, True)
    DELTA = (153, True)
    RHO = (154, True)
    EPSILON = (155, True)
    LAMBDA = (156, True)

    # SECOND ORDER GREEKS
    GAMMA = (161, True)
    VANNA = (162, True)
    CHARM = (163, True)
    VOMMA = (164, True)
    VETA = (165, True)
    VERA = (166, True)
    SOPDK = (167, True)

    # THIRD ORDER GREEKS
    SPEED = (171, True)
    ZOMMA = (172, True)
    COLOR = (173, True)
    ULTIMA = (174, True)

    # OTHER CALCS
    D1 = (181, True)
    D2 = (182, True)
    DUAL_DELTA = (183, True)
    DUAL_GAMMA = (184, True)

    # OHLC
    OPEN = (191, True)
    HIGH = (192, True)
    LOW = (193, True)
    CLOSE = (194, True)

    # IMPLIED VOLATILITY
    IMPLIED_VOL = (201, True)
    BID_IMPLIED_VOL = (202, True)
    ASK_IMPLIED_VOL = (203, True)
    UNDERLYING_PRICE = (204, True)

    # OTHER
    RATIO = (211, True)
    RATING = (212, True)

    # DIVIDEND
    EX_DATE = (221, False)
    RECORD_DATE = (222, False)
    PAYMENT_DATE = (223, False)
    ANN_DATE = (224, False)
    DIVIDEND_AMOUNT = (225, True)
    LESS_AMOUNT = (226, True)

    @classmethod
    def from_code(cls, code: int) -> DataType:
        """Create a DataType by its associated code.

        :raises EnumParseError: If the code does not match a DataType
        """
        for member in cls:
            if code == member.value[0]:
                return member
        raise exceptions._EnumParseError(code, cls)

    @classmethod
    def from_string(cls, name)-> DataType:
        """Create a DataType by its associated name.

        :raises EnumParseError: If the string does not match a DataType
        """
        for member in cls:
            if name == member.name.lower():
                return member
        raise exceptions._EnumParseError(name, cls)

    def code(self) -> int:
        """:return: The datatype code associated w this type."""
        return self.value[0]

    def is_price(self) -> bool:
        """Check if this DataType indicates a price."""
        return self.value[1]

code()

Returns:

Type Description
int

The datatype code associated w this type.

Source code in thetadata/enums.py
Python
def code(self) -> int:
    """:return: The datatype code associated w this type."""
    return self.value[0]

from_code(code) classmethod

Create a DataType by its associated code.

Raises:

Type Description
EnumParseError

If the code does not match a DataType

Source code in thetadata/enums.py
Python
@classmethod
def from_code(cls, code: int) -> DataType:
    """Create a DataType by its associated code.

    :raises EnumParseError: If the code does not match a DataType
    """
    for member in cls:
        if code == member.value[0]:
            return member
    raise exceptions._EnumParseError(code, cls)

from_string(name) classmethod

Create a DataType by its associated name.

Raises:

Type Description
EnumParseError

If the string does not match a DataType

Source code in thetadata/enums.py
Python
@classmethod
def from_string(cls, name)-> DataType:
    """Create a DataType by its associated name.

    :raises EnumParseError: If the string does not match a DataType
    """
    for member in cls:
        if name == member.name.lower():
            return member
    raise exceptions._EnumParseError(name, cls)

is_price()

Check if this DataType indicates a price.

Source code in thetadata/enums.py
Python
def is_price(self) -> bool:
    """Check if this DataType indicates a price."""
    return self.value[1]

DateRange dataclass

Represents an inclusive date range.

Source code in thetadata/enums.py
Python
@dataclass
class DateRange:
    """Represents an inclusive date range."""

    start: date
    end: date

    def __init__(self, start: date, end: date):
        """Create an inclusive date range between `start` and `end`."""
        assert isinstance(
            start, date
        ), f"Expected start to be a date object. Got {type(start)}."
        assert isinstance(
            start, date
        ), f"Expected end to be a date object. Got {type(end)}."
        self.start = start
        self.end = end
        assert (
            start <= end
        ), f"Start date {self.start} is greater than end date {self.end}!"

    @classmethod
    def from_days(cls, n: int) -> DateRange:
        """Create a date range that spans the past `n` days."""
        assert type(n) == int
        assert n >= 0, "n must be nonnegative"
        end = datetime.now().date()
        start = end - timedelta(days=n)
        return cls(start, end)

__init__(start, end)

Create an inclusive date range between start and end.

Source code in thetadata/enums.py
Python
def __init__(self, start: date, end: date):
    """Create an inclusive date range between `start` and `end`."""
    assert isinstance(
        start, date
    ), f"Expected start to be a date object. Got {type(start)}."
    assert isinstance(
        start, date
    ), f"Expected end to be a date object. Got {type(end)}."
    self.start = start
    self.end = end
    assert (
        start <= end
    ), f"Start date {self.start} is greater than end date {self.end}!"

from_days(n) classmethod

Create a date range that spans the past n days.

Source code in thetadata/enums.py
Python
@classmethod
def from_days(cls, n: int) -> DateRange:
    """Create a date range that spans the past `n` days."""
    assert type(n) == int
    assert n >= 0, "n must be nonnegative"
    end = datetime.now().date()
    start = end - timedelta(days=n)
    return cls(start, end)

Exchange

Bases: Enum

Codes used to ID types of requests/responses.

Source code in thetadata/enums.py
Python
@enum.unique
class Exchange(enum.Enum):
    """Codes used to ID types of requests/responses."""

    COMP = (0, "", "Comp")
    NQEX = (1, "XNMS", "Nasdaq Exchange")
    NQAD = (2, "XADF", "Nasdaq Alternative Display Facility")
    NYSE = (3, "XNYS", "New York Stock Exchange")
    AMEX = (4, "XASE", "American Stock Exchange")
    CBOE = (5, "XCBO", "Chicago Board Options Exchange")
    ISEX = (6, "XISX", "International Securities Exchange")
    PACF = (7, "ARCX", "NYSE ARCA (Pacific)")
    CINC = (8, "XCIS", "National Stock Exchange (Cincinnati)")
    PHIL = (9, "XPHL", "Philidelphia Stock Exchange")
    OPRA = (10, "OPRA", "Options Pricing Reporting Authority")
    BOST = (11, "XBOS", "Boston Stock/Options Exchange")
    NQNM = (12, "XNGS", "Nasdaq Global+Select Market (NMS)")
    NQSC = (13, "XNCM", "Nasdaq Capital Market (SmallCap)")
    NQBB = (14, "OOTC", "Nasdaq Bulletin Board")
    NQPK = (15, "OOTC", "Nasdaq OTC")
    NQAG = (16, "XADF", "Nasdaq Aggregate Quote")
    CHIC = (17, "CXHI", "Chicago Stock Exchange")
    TSE = (18, "XTSE", "Toronto Stock Exchange")
    CDNX = (19, "XTSX", "Canadian Venture Exchange")
    CME = (20, "XCME", "Chicago Mercantile Exchange")
    NYBT = (21, "IMAG", "New York Board of Trade")
    MRCY = (22, "MCRY", "ISE Mercury")
    COMX = (23, "XCEC", "COMEX (division of NYMEX)")
    CBOT = (24, "GLBX", "Chicago Board of Trade")
    NYMX = (25, "XNYM", "New York Mercantile Exchange")
    KCBT = (26, "XKBT", "Kansas City Board of Trade")
    MGEX = (27, "XMGE", "Minneapolis Grain Exchange")
    WCE = (28, "IFCA", "Winnipeg Commodity Exchange")
    ONEC = (29, "XOCH", "OneChicago Exchange")
    DOWJ = (30, "", "Dow Jones Indicies")
    GEMI = (31, "GMNI", "ISE Gemini")
    SIMX = (32, "XSES", "Singapore International Monetary Exchange")
    FTSE = (33, "XLON", "London Stock Exchange")
    EURX = (34, "XEUR", "Eurex")
    ENXT = (35, "XAMS", "EuroNext")
    DTN = (36, "", "Data Transmission Network")
    LMT = (37, "XLME", "London Metals Exchange Matched Trades")
    LME = (38, "XLME", "London Metals Exchange")
    IPEX = (39, "IEPA", "Intercontinental Exchange (IPE)")
    MX = (40, "XMOD", "Montreal Stock Exchange")
    WSE = (41, "XTSX", "Winnipeg Stock Exchange")
    C2 = (42, "C2OX", "CBOE C2 Option Exchange")
    MIAX = (43, "XMIO", "Miami Exchange")
    CLRP = (44, "XNYM", "NYMEX Clearport")
    BARK = (45, "BARX", "Barclays")
    TEN4 = (46, "", "TenFore")
    NQBX = (47, "XBOS", "NASDAQ Boston")
    HOTS = (48, "XEUR", "HotSpot Eurex US")
    EUUS = (49, "XEUR", "Eurex US")
    EUEU = (50, "XEUR", "Eurex EU")
    ENCM = (51, "XEUC", "Euronext Commodities")
    ENID = (52, "XEUE", "Euronext Index Derivatives")
    ENIR = (53, "XEUI", "Euronext Interest Rates")
    CFE = (54, "XCBF", "CBOE Futures Exchange")
    PBOT = (55, "XPBT", "Philadelphia Board of Trade")
    HWTB = (56, "XHAN", "Hannover WTB Exchange")
    NQNX = (57, "FINN", "FINRA/NASDAQ Trade Reporting Facility")
    BTRF = (58, "XADF", "BSE Trade Reporting Facility")
    NTRF = (59, "FINY", "NYSE Trade Reporting Facility")
    BATS = (60, "BATS", "BATS Trading")
    NYLF = (61, "XNLI", "NYSE LIFFE metals contracts")
    PINK = (62, "OTCM", "Pink Sheets")
    BATY = (63, "BATY", "BATS Trading")
    EDGE = (64, "EDGA", "Direct Edge")
    EDGX = (65, "EDGX", "Direct Edge")
    RUSL = (66, "", "Russell Indexes")
    CMEX = (67, "XIOM", "CME Indexes")
    IEX = (68, "IEXG", "Investors Exchange")
    PERL = (69, "PERL", "Miami Pearl Options Exchange")
    LSE = (70, "LSE", "London Stock Exchange")
    NYSE_GIF = (71, "NYSE_GIF", "NYSE Global Index Feed")
    TSX_IDX = (72, "TSX_IDX", "TSX Indexes")
    MEMX = (73, "MEMX", "Members Exchange")
    TBA_74 = (74, "TBA_74", "TBA Exchange 74")
    LTSE = (75, "Long Term Stock Exchange")
    TBA_76 = (76, "TBA_76", "TBA Exchange 76")
    TBA_77 = (77, "TBA_77", "TBA Exchange 77")
    TBA_78 = (78, "TBA_78", "TBA Exchange 78")
    TBA_79 = (79, "TBA_79", "TBA Exchange 79")

    @classmethod
    def from_code(cls, code: int) -> Exchange:
        """Create a MessageType by its associated code.

        :raises EnumParseError: If the code does not match a MessageType
        """
        for member in cls:
            if code == member.value[0]:
                return member
        raise exceptions._EnumParseError(code, cls)

from_code(code) classmethod

Create a MessageType by its associated code.

Raises:

Type Description
EnumParseError

If the code does not match a MessageType

Source code in thetadata/enums.py
Python
@classmethod
def from_code(cls, code: int) -> Exchange:
    """Create a MessageType by its associated code.

    :raises EnumParseError: If the code does not match a MessageType
    """
    for member in cls:
        if code == member.value[0]:
            return member
    raise exceptions._EnumParseError(code, cls)

MessageType

Bases: Enum

Codes used to ID types of requests/responses.

Source code in thetadata/enums.py
Python
@enum.unique
class MessageType(enum.Enum):
    """Codes used to ID types of requests/responses."""

    # Internal client communication
    CREDENTIALS = 0
    SESSION_TOKEN = 1
    INFO = 2
    METADATA = 3
    CONNECTED = 4
    VERSION = 5

    # API communication
    PING = 100
    ERROR = 101
    DISCONNECTED = 102
    RECONNECTED = 103
    REQ_SYMS = 104
    SET_SYMS = 105
    CANT_CHANGE_SYMS = 106
    CHANGED_SYMS = 107
    KILL = 108

    # Client data
    HIST = 200
    ALL_EXPIRATIONS = 201
    ALL_STRIKES = 202
    HIST_END = 203
    LAST = 204
    ALL_ROOTS = 205
    LIST_END = 206
    ALL_DATES = 207
    AT_TIME = 208
    ALL_DATES_BULK = 209
    STREAM_REQ = 210
    STREAM_CALLBACK = 211
    STREAM_REMOVE = 212

    # Experimental
    REQUEST_SERVER_LIST = 300
    REQUEST_OPTIMAL_SERVER = 301
    OPTIMAL_SERVER = 302
    PACKET = 303
    BAN_IP = 304
    POPULATION = 305

    @classmethod
    def from_code(cls, code: int) -> MessageType:
        """Create a MessageType by its associated code.

        :raises EnumParseError: If the code does not match a MessageType
        """
        for member in cls:
            if code == member.value:
                return member
        raise exceptions._EnumParseError(code, cls)

from_code(code) classmethod

Create a MessageType by its associated code.

Raises:

Type Description
EnumParseError

If the code does not match a MessageType

Source code in thetadata/enums.py
Python
@classmethod
def from_code(cls, code: int) -> MessageType:
    """Create a MessageType by its associated code.

    :raises EnumParseError: If the code does not match a MessageType
    """
    for member in cls:
        if code == member.value:
            return member
    raise exceptions._EnumParseError(code, cls)

OptionReqType

Bases: Enum

Option request type codes.

https://www.thetadata.net/request-types

Source code in thetadata/enums.py
Python
@enum.unique
class OptionReqType(enum.Enum):
    """
    Option request type codes.

    https://www.thetadata.net/request-types
    """

    # FREE
    EOD = 1
    """
    End-Of-Day OHLC trade bars.

    Returned columns:

    DataType.DATE
    DataType.OPEN
    DataType.HIGH
    DataType.LOW
    DataType.CLOSE
    DataType.VOLUME - number of contracts traded
    DataType.COUNT - number of trades
    """

    # VALUE
    QUOTE = 101
    """
    NBBO quotes with millisecond timestamps.

    Either all quotes, if no interval specified, or interval-length snapshots (not OHLC).

    Returned columns:

    DataType.DATE
    DataType.MS_OF_DAY - Snapshot time. Milliseconds since 00:00.000 ET.
    DataType.BID_SIZE
    DataType.BID_CONDITION
    DataType.BID
    DataType.BID_EXCHANGE
    DataType.ASK_SIZE
    DataType.ASK_CONDITION
    DataType.ASK
    DataType.ASK_EXCHANGE
    """

    VOLUME = 102
    OPEN_INTEREST = 103

    OHLC = 104
    """
    Interval-length OHLC trade bars. Interval must be provided.

    Similar to EOD trade bars except with custom interval.

    Returned columns:

    DataType.DATE
    DataType.MS_OF_DAY - Start of the interval. Milliseconds since 00:00.000 ET.
    DataType.OPEN
    DataType.HIGH
    DataType.LOW
    DataType.CLOSE
    DataType.VOLUME - number of contracts traded
    DataType.COUNT - number of trades
    """

    OHLC_QUOTE = 105
    """
    Interval-length OHLC quote bars. Interval must be provided.

    Prices are computed using bid/ask midpoint values.

    Returned columns:

    DataType.DATE
    DataType.MS_OF_DAY - Start of the interval. Milliseconds since 00:00.000 ET.
    DataType.OPEN
    DataType.HIGH
    DataType.LOW
    DataType.CLOSE
    DataType.VOLUME - cumulative bid/ask size within interval
    DataType.COUNT - number of quotes within interval
    """

    # STANDARD
    TRADE = 201
    IMPLIED_VOLATILITY = 202
    GREEKS = 203
    LIQUIDITY = 204
    LIQUIDITY_PLUS = 205
    IMPLIED_VOLATILITY_VERBOSE = 206
    TRADE_QUOTE = 207
    EOD_QUOTE_GREEKS = 208
    EOD_TRADE_GREEKS = 209

    # PRO
    TRADE_GREEKS = 301
    GREEKS_SECOND_ORDER = 302
    GREEKS_THIRD_ORDER = 303
    ALT_CALCS = 304
    TRADE_GREEKS_SECOND_ORDER = 305
    TRADE_GREEKS_THIRD_ORDER = 306

EOD = 1 class-attribute instance-attribute

End-Of-Day OHLC trade bars.

Returned columns:

DataType.DATE DataType.OPEN DataType.HIGH DataType.LOW DataType.CLOSE DataType.VOLUME - number of contracts traded DataType.COUNT - number of trades

OHLC = 104 class-attribute instance-attribute

Interval-length OHLC trade bars. Interval must be provided.

Similar to EOD trade bars except with custom interval.

Returned columns:

DataType.DATE DataType.MS_OF_DAY - Start of the interval. Milliseconds since 00:00.000 ET. DataType.OPEN DataType.HIGH DataType.LOW DataType.CLOSE DataType.VOLUME - number of contracts traded DataType.COUNT - number of trades

OHLC_QUOTE = 105 class-attribute instance-attribute

Interval-length OHLC quote bars. Interval must be provided.

Prices are computed using bid/ask midpoint values.

Returned columns:

DataType.DATE DataType.MS_OF_DAY - Start of the interval. Milliseconds since 00:00.000 ET. DataType.OPEN DataType.HIGH DataType.LOW DataType.CLOSE DataType.VOLUME - cumulative bid/ask size within interval DataType.COUNT - number of quotes within interval

QUOTE = 101 class-attribute instance-attribute

NBBO quotes with millisecond timestamps.

Either all quotes, if no interval specified, or interval-length snapshots (not OHLC).

Returned columns:

DataType.DATE DataType.MS_OF_DAY - Snapshot time. Milliseconds since 00:00.000 ET. DataType.BID_SIZE DataType.BID_CONDITION DataType.BID DataType.BID_EXCHANGE DataType.ASK_SIZE DataType.ASK_CONDITION DataType.ASK DataType.ASK_EXCHANGE

OptionRight

Bases: Enum

Option rights.

Source code in thetadata/enums.py
Python
@enum.unique
class OptionRight(enum.Enum):
    """Option rights."""

    PUT = "P"
    CALL = "C"

QuoteCondition

Bases: Enum

Codes used to ID types of requests/responses.

Source code in thetadata/enums.py
Python
@enum.unique
class QuoteCondition(enum.Enum):
    """Codes used to ID types of requests/responses."""

    REGULAR = 0
    BID_ASK_AUTO_EXEC = 1
    ROTATION = 2
    SPECIALIST_ASK = 3
    SPECIALIST_BID = 4
    LOCKED = 5
    FAST_MARKET = 6
    SPECIALIST_BID_ASK = 7
    ONE_SIDE = 8
    OPENING_QUOTE = 9
    CLOSING_QUOTE = 10
    MARKET_MAKER_CLOSED = 11
    DEPTH_ON_ASK = 12
    DEPTH_ON_BID = 13
    DEPTH_ON_BID_ASK = 14
    TIER_3 = 15
    CROSSED = 16
    HALTED = 17
    OPERATIONAL_HALT = 18
    NEWS = 19
    NEWS_PENDING = 20
    NON_FIRM = 21
    DUE_TO_RELATED = 22
    RESUME = 23
    NO_MARKET_MAKERS = 24
    ORDER_IMBALANCE = 25
    ORDER_INFLUX = 26
    INDICATED = 27
    PRE_OPEN = 28
    IN_VIEW_OF_COMMON = 29
    RELATED_NEWS_PENDING = 30
    RELATED_NEWS_OUT = 31
    ADDITIONAL_INFO = 32
    RELATED_ADDL_INFO = 33
    NO_OPEN_RESUME = 34
    DELETED = 35
    REGULATORY_HALT = 36
    SEC_SUSPENSION = 37
    NON_COMLIANCE = 38
    FILINGS_NOT_CURRENT = 39
    CATS_HALTED = 40
    CATS = 41
    EX_DIV_OR_SPLIT = 42
    UNASSIGNED = 43
    INSIDE_OPEN = 44
    INSIDE_CLOSED = 45
    OFFER_WANTED = 46
    BID_WANTED = 47
    CASH = 48
    INACTIVE = 49
    NATIONAL_BBO = 50
    NOMINAL = 51
    CABINET = 52
    NOMINAL_CABINET = 53
    BLANK_PRICE = 54
    SLOW_BID_ASK = 55
    SLOW_LIST = 56
    SLOW_BID = 57
    SLOW_ASK = 58
    BID_OFFER_WANTED = 59
    SUB_PENNY = 60
    NON_BBO = 61
    TBA_62 = 62
    TBA_63 = 63
    TBA_64 = 64
    TBA_65 = 65
    TBA_66 = 66
    TBA_67 = 67
    TBA_68 = 68
    TBA_69 = 69
    UNDEFINED = 10000

    @classmethod
    def from_code(cls, code: int) -> QuoteCondition:
        """Create a MessageType by its associated code.

        :raises EnumParseError: If the code does not match a MessageType
        """
        for member in cls:
            if code == member.value:
                return member
        return QuoteCondition.UNDEFINED

from_code(code) classmethod

Create a MessageType by its associated code.

Raises:

Type Description
EnumParseError

If the code does not match a MessageType

Source code in thetadata/enums.py
Python
@classmethod
def from_code(cls, code: int) -> QuoteCondition:
    """Create a MessageType by its associated code.

    :raises EnumParseError: If the code does not match a MessageType
    """
    for member in cls:
        if code == member.value:
            return member
    return QuoteCondition.UNDEFINED

SecType

Bases: Enum

Security types.

Source code in thetadata/enums.py
Python
@enum.unique
class SecType(enum.Enum):
    """Security types."""

    OPTION = "OPTION"
    STOCK = "STOCK"
    FUTURE = "FUTURE"
    FORWARD = "FORWARD"
    SWAP = "SWAP"
    DEBT = "DEBT"
    CRYPTO = "CRYPTO"
    WARRANT = "WARRANT"

StockReqType

Bases: Enum

Stock request type codes.

Source code in thetadata/enums.py
Python
@enum.unique
class StockReqType(enum.Enum):
    """Stock request type codes."""

    # FREE
    EOD = 1

    # VALUE
    QUOTE = 101
    VOLUME = 102
    OHLC = 104

    # STANDARD
    TRADE = 201

StreamMsgType

Bases: Enum

Codes used to ID types of requests/responses.

Source code in thetadata/enums.py
Python
@enum.unique
class StreamMsgType(enum.Enum):
    """Codes used to ID types of requests/responses."""

    # Internal client communication
    CREDENTIALS = 0
    SESSION_TOKEN = 1
    INFO = 2
    METADATA = 3
    CONNECTED = 4

    # API communication
    PING = 10
    ERROR = 11
    DISCONNECTED = 12
    RECONNECTED = 13
    STREAM_DEAD = 14

    # Client data
    CONTRACT = 20
    QUOTE = 21
    TRADE = 22
    OPEN_INTEREST = 23
    OHLCVC = 24
    # Tape commands
    START = 30
    RESTART = 31
    STOP = 32

    # Misc
    REQ_RESPONSE = 40

    @classmethod
    def from_code(cls, code: int) -> StreamMsgType:
        """Create a MessageType by its associated code.

        :raises EnumParseError: If the code does not match a MessageType
        """
        for member in cls:
            if code == member.value:
                return member
        raise exceptions._EnumParseError(code, cls)

from_code(code) classmethod

Create a MessageType by its associated code.

Raises:

Type Description
EnumParseError

If the code does not match a MessageType

Source code in thetadata/enums.py
Python
@classmethod
def from_code(cls, code: int) -> StreamMsgType:
    """Create a MessageType by its associated code.

    :raises EnumParseError: If the code does not match a MessageType
    """
    for member in cls:
        if code == member.value:
            return member
    raise exceptions._EnumParseError(code, cls)

StreamResponseType

Bases: Enum

Codes used to ID types of requests/responses.

Source code in thetadata/enums.py
Python
@enum.unique
class StreamResponseType(enum.Enum):
    """Codes used to ID types of requests/responses."""

    # Internal client communication
    SUBSCRIBED = 0  # This doesn't guarantee you will get data back for the contract. It just means that if this contract exists, you will get data for it.
    TIMED_OUT = 1  # The request to stream something timed out.
    MAX_STREAMS_REACHED = 2  # Returned when you are streaming too many contracts.
    INVALID_PERMS = 3  # If you do not have permissions for the stream request.

    @classmethod
    def from_code(cls, code: int) -> StreamResponseType:
        """Create a MessageType by its associated code.

        :raises EnumParseError: If the code does not match a MessageType
        """
        for member in cls:
            if code == member.value:
                return member
        raise exceptions._EnumParseError(code, cls)

from_code(code) classmethod

Create a MessageType by its associated code.

Raises:

Type Description
EnumParseError

If the code does not match a MessageType

Source code in thetadata/enums.py
Python
@classmethod
def from_code(cls, code: int) -> StreamResponseType:
    """Create a MessageType by its associated code.

    :raises EnumParseError: If the code does not match a MessageType
    """
    for member in cls:
        if code == member.value:
            return member
    raise exceptions._EnumParseError(code, cls)

TradeCondition

Bases: Enum

Codes used to ID types of requests/responses.

Source code in thetadata/enums.py
Python
@enum.unique
class TradeCondition(enum.Enum):
    """Codes used to ID types of requests/responses."""

    REGULAR = 0
    FORM_T = 1
    OUT_OF_SEQ = 2
    AVG_PRC = 3
    AVG_PRC_NASDAQ = 4
    OPEN_REPORT_LATE = 5
    OPEN_REPORT_OUT_OF_SEQ = 6
    OPEN_REPORT_IN_SEQ = 7
    PRIOR_REFERENCE_PRICE = 8
    NEXT_DAY_SALE = 9
    BUNCHED = 10
    CASH_SALE = 11
    SELLER = 12
    SOLD_LAST = 13
    RULE_127 = 14
    BUNCHED_SOLD = 15
    NON_BOARD_LOT = 16
    POSIT = 17
    AUTO_EXECUTION = 18
    HALT = 19
    DELAYED = 20
    REOPEN = 21
    ACQUISITION = 22
    CASH_MARKET = 23
    NEXT_DAY_MARKET = 24
    BURST_BASKET = 25
    OPEN_DETAIL = 26
    INTRA_DETAIL = 27
    BASKET_ON_CLOSE = 28
    RULE_155 = 29
    DISTRIBUTION = 30
    SPLIT = 31
    RESERVED = 32
    CUSTOM_BASKET_CROSS = 33
    ADJ_TERMS = 34
    SPREAD = 35
    STRADDLE = 36
    BUY_WRITE = 37
    COMBO = 38
    STPD = 39
    CANC = 40
    CANC_LAST = 41
    CANC_OPEN = 42
    CANC_ONLY = 43
    CANC_STPD = 44
    MATCH_CROSS = 45
    FAST_MARKET = 46
    NOMINAL = 47
    CABINET = 48
    BLANK_PRICE = 49
    NOT_SPECIFIED = 50
    MC_OFFICIAL_CLOSE = 51
    SPECIAL_TERMS = 52
    CONTINGENT_ORDER = 53
    INTERNAL_CROSS = 54
    STOPPED_REGULAR = 55
    STOPPED_SOLD_LAST = 56
    STOPPED_OUT_OF_SEQ = 57
    BASIS = 58
    VWAP = 59
    SPECIAL_SESSION = 60
    NANEX_ADMIN = 61
    OPEN_REPORT = 62
    MARKET_ON_CLOSE = 63
    NOT_DEFINED = 64
    OUT_OF_SEQ_PRE_MKT = 65
    MC_OFFICIAL_OPEN = 66
    FUTURES_SPREAD = 67
    OPEN_RANGE = 68
    CLOSE_RANGE = 69
    NOMINAL_CABINET = 70
    CHANGING_TRANS = 71
    CHANGING_TRANS_CAB = 72
    NOMINAL_UPDATE = 73
    PIT_SETTLEMENT = 74
    BLOCK_TRADE = 75
    EXG_FOR_PHYSICAL = 76
    VOLUME_ADJUSTMENT = 77
    VOLATILITY_TRADE = 78
    YELLOW_FLAG = 79
    FLOOR_PRICE = 80
    OFFICIAL_PRICE = 81
    UNOFFICIAL_PRICE = 82
    MID_BID_ASK_PRICE = 83
    END_SESSION_HIGH = 84
    END_SESSION_LOW = 85
    BACKWARDATION = 86
    CONTANGO = 87
    HOLIDAY = 88
    PRE_OPENING = 89
    POST_FULL = 90
    POST_RESTRICTED = 91
    CLOSING_AUCTION = 92
    BATCH = 93
    TRADING = 94
    INTERMARKET_SWEEP = 95
    DERIVATIVE = 96
    REOPENING = 97
    CLOSING = 98
    CAP_ELECTION = 99
    SPOT_SETTLEMENT = 100
    BASIS_HIGH = 101
    BASIS_LOW = 102
    YIELD = 103
    PRICE_VARIATION = 104
    STOCK_OPTION = 105
    STOPPED_IM = 106
    BENCHMARK = 107
    TRADE_THRU_EXEMPT = 108
    IMPLIED = 109
    OTC = 110
    MKT_SUPERVISION = 111
    RESERVED_77 = 112
    RESERVED_91 = 113
    CONTINGENT_UTP = 114
    ODD_LOT = 115
    RESERVED_89 = 116
    CORRECTED_LAST = 117
    OPRA_EXT_HOURS = 118
    RESERVED_78 = 119
    RESERVED_81 = 120
    RESERVED_84 = 121
    RESERVED_878 = 122
    RESERVED_90 = 123
    QUALIFIED_CONTINGENT_TRADE = 124
    SINGLE_LEG_AUCTION_NON_ISO = 125
    SINGLE_LEG_AUCTION_ISO = 126
    SINGLE_LEG_CROSS_NON_ISO = 127
    SINGLE_LEG_CROSS_ISO = 128
    SINGLE_LEG_FLOOR_TRADE = 129
    MULTI_LEG_AUTO_ELECTRONIC_TRADE = 130
    MULTI_LEG_AUCTION = 131
    MULTI_LEG_CROSS = 132
    MULTI_LEG_FLOOR_TRADE = 133
    MULTI_LEG_AUTO_ELEC_TRADE_AGAINST_SINGLE_LEG = 134
    STOCK_OPTIONS_AUCTION = 135
    MULTI_LEG_AUCTION_AGAINST_SINGLE_LEG = 136
    MULTI_LEG_FLOOR_TRADE_AGAINST_SINGLE_LEG = 137
    STOCK_OPTIONS_AUTO_ELEC_TRADE = 138
    STOCK_OPTIONS_CROSS = 139
    STOCK_OPTIONS_FLOOR_TRADE = 140
    STOCK_OPTIONS_AUTO_ELEC_TRADE_AGAINST_SINGLE_LEG = 141
    STOCK_OPTIONS_AUCTION_AGAINST_SINGLE_LEG = 142
    STOCK_OPTIONS_FLOOR_TRADE_AGAINST_SINGLE_LEG = 143
    MULTI_LEG_FLOOR_TRADE_OF_PROPRIETARY_PRODUCTS = 144
    BID_AGGRESSOR = 145
    ASK_AGGRESSOR = 146
    MULTI_LATERAL_COMPRESSION_TRADE_OF_PROPRIETARY_DATA_PRODUCTS = 147
    EXTENDED_HOURS_TRADE = 148
    UNDEFINED = 10000

    @classmethod
    def from_code(cls, code: int) -> TradeCondition:
        """Create a MessageType by its associated code.

        :raises EnumParseError: If the code does not match a MessageType
        """
        for member in cls:
            if code == member.value:
                return member
        return TradeCondition.UNDEFINED

from_code(code) classmethod

Create a MessageType by its associated code.

Raises:

Type Description
EnumParseError

If the code does not match a MessageType

Source code in thetadata/enums.py
Python
@classmethod
def from_code(cls, code: int) -> TradeCondition:
    """Create a MessageType by its associated code.

    :raises EnumParseError: If the code does not match a MessageType
    """
    for member in cls:
        if code == member.value:
            return member
    return TradeCondition.UNDEFINED

thetadata.exceptions

Module that contains custom exceptions.

NoData

Bases: Exception

Raised if no data is available for this request.

Source code in thetadata/exceptions.py
Python
class NoData(Exception):
    """Raised if no data is available for this request."""

ReconnectingToServer

Bases: Exception

Raised if the connection has been lost to Theta Data and a reconnection attempt is being made.

Source code in thetadata/exceptions.py
Python
class ReconnectingToServer(Exception):
    """Raised if the connection has been lost to Theta Data and a reconnection attempt is being made."""

ResponseError

Bases: Exception

Raised if there is an error in the body of a response.

Source code in thetadata/exceptions.py
Python
class ResponseError(Exception):
    """Raised if there is an error in the body of a response."""

ResponseParseError

Bases: Exception

Raised if the API failed to parse a Terminal response.

Source code in thetadata/exceptions.py
Python
class ResponseParseError(Exception):
    """Raised if the API failed to parse a Terminal response."""