|
88 | 88 | OrderKeyType: TypeAlias = int | tuple[int, int]
|
89 | 89 | TickDict: TypeAlias = dict[int, str]
|
90 | 90 |
|
| 91 | +PRICE_TICK_MAP: Final[TickDict] = { |
| 92 | + 6: "high", |
| 93 | + 72: "high", |
| 94 | + 7: "low", |
| 95 | + 73: "low", |
| 96 | + 9: "close", |
| 97 | + 75: "close", |
| 98 | + 14: "open", |
| 99 | + 76: "open", |
| 100 | + 15: "low13week", |
| 101 | + 16: "high13week", |
| 102 | + 17: "low26week", |
| 103 | + 18: "high26week", |
| 104 | + 19: "low52week", |
| 105 | + 20: "high52week", |
| 106 | + 35: "auctionPrice", |
| 107 | + 37: "markPrice", |
| 108 | + 50: "bidYield", |
| 109 | + 103: "bidYield", |
| 110 | + 51: "askYield", |
| 111 | + 104: "askYield", |
| 112 | + 52: "lastYield", |
| 113 | +} |
| 114 | + |
| 115 | + |
| 116 | +SIZE_TICK_MAP: Final[TickDict] = { |
| 117 | + 8: "volume", |
| 118 | + 74: "volume", |
| 119 | + 63: "volumeRate3Min", |
| 120 | + 64: "volumeRate5Min", |
| 121 | + 65: "volumeRate10Min", |
| 122 | + 21: "avVolume", |
| 123 | + 27: "callOpenInterest", |
| 124 | + 28: "putOpenInterest", |
| 125 | + 29: "callVolume", |
| 126 | + 30: "putVolume", |
| 127 | + 34: "auctionVolume", |
| 128 | + 36: "auctionImbalance", |
| 129 | + 61: "regulatoryImbalance", |
| 130 | + 86: "futuresOpenInterest", |
| 131 | + 87: "avOptionVolume", |
| 132 | + 89: "shortableShares", |
| 133 | +} |
| 134 | + |
91 | 135 | GENERIC_TICK_MAP: Final[TickDict] = {
|
92 | 136 | 23: "histVolatility",
|
93 | 137 | 24: "impliedVolatility",
|
@@ -860,36 +904,12 @@ def priceSizeTick(self, reqId: int, tickType: int, price: float, size: float):
|
860 | 904 | if size != ticker.lastSize:
|
861 | 905 | ticker.prevLastSize = ticker.lastSize
|
862 | 906 | ticker.lastSize = size
|
863 |
| - elif tickType in {6, 72}: |
864 |
| - ticker.high = price |
865 |
| - elif tickType in {7, 73}: |
866 |
| - ticker.low = price |
867 |
| - elif tickType in {9, 75}: |
868 |
| - ticker.close = price |
869 |
| - elif tickType in {14, 76}: |
870 |
| - ticker.open = price |
871 |
| - elif tickType == 15: |
872 |
| - ticker.low13week = price |
873 |
| - elif tickType == 16: |
874 |
| - ticker.high13week = price |
875 |
| - elif tickType == 17: |
876 |
| - ticker.low26week = price |
877 |
| - elif tickType == 18: |
878 |
| - ticker.high26week = price |
879 |
| - elif tickType == 19: |
880 |
| - ticker.low52week = price |
881 |
| - elif tickType == 20: |
882 |
| - ticker.high52week = price |
883 |
| - elif tickType == 35: |
884 |
| - ticker.auctionPrice = price |
885 |
| - elif tickType == 37: |
886 |
| - ticker.markPrice = price |
887 |
| - elif tickType in {50, 103}: |
888 |
| - ticker.bidYield = price |
889 |
| - elif tickType in {51, 104}: |
890 |
| - ticker.askYield = price |
891 |
| - elif tickType == 52: |
892 |
| - ticker.lastYield = price |
| 907 | + else: |
| 908 | + assert ( |
| 909 | + tickType in PRICE_TICK_MAP |
| 910 | + ), f"Received tick {tickType=} {price=} but we don't have an attribute mapping for it? Triggered from {ticker.contract=}" |
| 911 | + |
| 912 | + setattr(ticker, PRICE_TICK_MAP[tickType], price) |
893 | 913 |
|
894 | 914 | if price or size:
|
895 | 915 | tick = TickData(self.lastTime, tickType, price, size)
|
@@ -924,30 +944,12 @@ def tickSize(self, reqId: int, tickType: int, size: float):
|
924 | 944 | if size != ticker.lastSize:
|
925 | 945 | ticker.prevLastSize = ticker.lastSize
|
926 | 946 | ticker.lastSize = size
|
927 |
| - elif tickType in {8, 74}: |
928 |
| - ticker.volume = size |
929 |
| - elif tickType == 21: |
930 |
| - ticker.avVolume = size |
931 |
| - elif tickType == 27: |
932 |
| - ticker.callOpenInterest = size |
933 |
| - elif tickType == 28: |
934 |
| - ticker.putOpenInterest = size |
935 |
| - elif tickType == 29: |
936 |
| - ticker.callVolume = size |
937 |
| - elif tickType == 30: |
938 |
| - ticker.putVolume = size |
939 |
| - elif tickType == 34: |
940 |
| - ticker.auctionVolume = size |
941 |
| - elif tickType == 36: |
942 |
| - ticker.auctionImbalance = size |
943 |
| - elif tickType == 61: |
944 |
| - ticker.regulatoryImbalance = size |
945 |
| - elif tickType == 86: |
946 |
| - ticker.futuresOpenInterest = size |
947 |
| - elif tickType == 87: |
948 |
| - ticker.avOptionVolume = size |
949 |
| - elif tickType == 89: |
950 |
| - ticker.shortableShares = size |
| 947 | + else: |
| 948 | + assert ( |
| 949 | + tickType in SIZE_TICK_MAP |
| 950 | + ), f"Received tick {tickType=} {price=} but we don't have an attribute mapping for it? Triggered from {ticker.contract=}" |
| 951 | + |
| 952 | + setattr(ticker, SIZE_TICK_MAP[tickType], price) |
951 | 953 |
|
952 | 954 | if price or size:
|
953 | 955 | tick = TickData(self.lastTime, tickType, price, size)
|
@@ -1054,6 +1056,10 @@ def tickString(self, reqId: int, tickType: int, value: str):
|
1054 | 1056 | ticker.askExchange = value
|
1055 | 1057 | elif tickType == 84:
|
1056 | 1058 | ticker.lastExchange = value
|
| 1059 | + elif tickType == 45: |
| 1060 | + ticker.lastTimestamp = datetime.fromtimestamp( |
| 1061 | + int(value), self.defaultTimezone |
| 1062 | + ) |
1057 | 1063 | elif tickType == 47:
|
1058 | 1064 | # https://web.archive.org/web/20200725010343/https://interactivebrokers.github.io/tws-api/fundamental_ratios_tags.html
|
1059 | 1065 | d = dict(
|
|
0 commit comments