Skip to content

Commit 2dc26ac

Browse files
authored
Support Deephaven 0.10.0 (#31)
* Change DynamicTableWriter from using logRow to logRowPermissive. * Added missing error code. * Use int64 instead of int32 for column types. * Debug logging for deadlocks * Rerequest IDs, in case they have not been sent. * Debug logging for deadlocks * Rerequest IDs, in case they have not been sent. * Added thread names. * Add stack traces for all threads. * Generalizing the strategy for obtaining order ids. * Make the strategy for getting order IDs selectable. * Fixing bad import * Fixing bad import * Fixing bad import * Fix broken enum * Cleaned up debugging output * Update docs * Update error messages * Add type annotation * Added space to error message. * Fixed thread name.
1 parent f7289d0 commit 2dc26ac

File tree

9 files changed

+167
-83
lines changed

9 files changed

+167
-83
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ See [Access your file system with Docker data volumes](https://deephaven.io/core
144144

145145
Follow these steps to run a [Deephaven](https://deephaven.io) plus [Interactive Brokers](https://interactivebrokers.com) system.
146146

147-
`<deephaven_version>` is the version of [Deephaven](https://deephaven.io) to run (e.g., `0.9.0`). A list of available versions
147+
`<deephaven_version>` is the version of [Deephaven](https://deephaven.io) to run (e.g., `0.10.0`). A list of available versions
148148
can be found on the [Deephaven Releases GitHub page](https://github.com/deephaven/deephaven-core/releases).
149149

150150
**Windows users need to run the commands in WSL.**
@@ -219,6 +219,11 @@ of Docker, `host` should be set to `host.docker.internal`.
219219
communicates on. This value can be found in the [IB Trader Workstation (TWS)](https://www.interactivebrokers.com/en/trading/tws.php)
220220
settings. By default, production trading uses port 7496, and paper trading uses port 7497. See [Setup](#setup) and [TWS Initial Setup](https://interactivebrokers.github.io/tws-api/initial_setup.html) for more details.
221221

222+
`order_id_strategy` is the strategy used for obtaining new order ids.
223+
* `OrderIdStrategy.RETRY` (default) - Request a new order ID from TWS every time one is needed. Retry if TWS does not respond quickly. This usually avoids a TWS bug where it does not always respond.
224+
* `OrderIdStrategy.BASIC` - Request a new order ID from TWS every time one is needed. Does not retry, so it may deadlock if TWS does not respond.
225+
* `OrderIdStrategy.INCREMENT` - Use the initial order ID sent by TWS and increment the value upon every request. This is fast, but it may fail for multiple, concurrent sessions connected to TWS.
226+
222227
```python
223228
import deephaven_ib as dhib
224229

src/deephaven_ib/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
from ._query_inputs import *
1010
from ._tws import IbTwsClient
11+
from ._tws.order_id_queue import OrderIdStrategy
1112
from .time import dh_to_ib_datetime
1213

13-
__all__ = ["MarketDataType", "TickDataType", "BarDataType", "BarSize", "Duration", "Request", "RegisteredContract",
14-
"IbSessionTws"]
14+
__all__ = ["MarketDataType", "TickDataType", "BarDataType", "BarSize", "Duration", "OrderIdStrategy",
15+
"Request", "RegisteredContract", "IbSessionTws"]
1516

1617

1718
class MarketDataType(Enum):
@@ -317,6 +318,8 @@ class IbSessionTws:
317318
318319
**NOTE: Each client MUST connect with a unique clientId.**
319320
download_short_rates (bool): True to download a short rates table.
321+
order_id_strategy (OrderIdStrategy): strategy for obtaining new order ids.
322+
320323
321324
Tables:
322325
####
@@ -390,11 +393,11 @@ class IbSessionTws:
390393
_tables_raw: Dict[str, Any] # TODO: should be Dict[str, Table] with deephaven v2
391394
_tables: Dict[str, Any] # TODO: should be Dict[str, Table] with deephaven v2
392395

393-
def __init__(self, host: str = "", port: int = 7497, client_id: int = 0, download_short_rates=True):
396+
def __init__(self, host: str = "", port: int = 7497, client_id: int = 0, download_short_rates: bool = True, order_id_strategy: OrderIdStrategy = OrderIdStrategy.RETRY):
394397
self._host = host
395398
self._port = port
396399
self._client_id = client_id
397-
self._client = IbTwsClient(download_short_rates=download_short_rates)
400+
self._client = IbTwsClient(download_short_rates=download_short_rates, order_id_strategy=order_id_strategy)
398401
self._tables_raw = {f"raw_{k}": v for k, v in self._client.tables.items()}
399402
self._tables = dict(sorted(IbSessionTws._make_tables(self._tables_raw).items()))
400403

src/deephaven_ib/_internal/error_codes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def load_error_codes() -> Tuple[Dict[int, str], Dict[int, str]]:
3434
502: "Couldn't connect to TWS. Confirm that 'Enable ActiveX and Socket EClients' is enabled and connection port is the same as 'Socket Port' on the TWS 'Edit->Global Configuration...->API->Settings' menu. Live Trading ports: TWS: 7496; IB Gateway: 4001. Simulated Trading ports for new installations of version 954.1 or newer: TWS: 7497; IB Gateway: 4002",
3535
2113: "The order size for Bonds (Bills) is entered as a nominal par value of the order, and must be a multiple",
3636
10089: "Requested market data requires additional subscription for API.See link in 'Market Data Connections' dialog for more details.",
37+
10172: "Failed to request news article: No data available",
3738
10187: "Failed to request historical ticks",
3839
10189: "Failed to request tick-by-tick data",
3940
}

src/deephaven_ib/_internal/tablewriter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def _check_logged_value_types(self, values: List) -> None:
5151
continue
5252

5353
if (t is dht.string and not isinstance(v, str)) or \
54-
(t is dht.int32 and not isinstance(v, int)) or \
54+
(t is dht.int64 and not isinstance(v, int)) or \
5555
(t is dht.float64 and not isinstance(v, float)):
5656
logging.error(
5757
f"TableWriter column type and value type are mismatched: column_name={n} column_type={t} value_type={type(v)} value={v}\n{trace_str()}\n-----")
@@ -73,7 +73,7 @@ def write_row(self, values: List) -> None:
7373
if values[i] == "":
7474
values[i] = None
7575

76-
self._dtw.logRow(values)
76+
self._dtw.logRowPermissive(values)
7777

7878

7979
ArrayStringSet = jpy.get_type("io.deephaven.stringset.ArrayStringSet")

src/deephaven_ib/_internal/threading.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def __init__(self, timeout_sec: float, sleep_sec: float):
2424
self.locks = {}
2525
self._lock = threading.Lock()
2626

27-
self._thread = threading.Thread(target=self._run, daemon=True)
27+
self._thread = threading.Thread(name="DeadlockMonitor", target=self._run, daemon=True)
2828
self._thread.start()
2929
setattr(self, "deadlock_monitor_thread", self._thread)
3030

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,27 @@
11
"""Functionality for working with stack traces."""
22

33
import traceback
4-
4+
import threading
5+
import sys
56

67
def trace_str() -> str:
78
"""Gets a string of the current stacktrace."""
89
return "".join(traceback.format_stack())
10+
11+
12+
def trace_thread_str(thread:threading.Thread) -> str:
13+
"""Gets a string of the stacktrace of a thread."""
14+
return "".join(traceback.format_stack(sys._current_frames()[thread.ident]))
15+
16+
17+
def trace_all_threads_str() -> str:
18+
"""Get the stacktraces for all threads as a string."""
19+
20+
rst = "Stack Traces:\n"
21+
22+
for th in threading.enumerate():
23+
rst += str(th)
24+
rst += trace_thread_str(th)
25+
rst += "\n"
26+
27+
return rst

src/deephaven_ib/_tws/ib_type_logger.py

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def map_right(right: str) -> Union[str, None]:
8181
return right
8282

8383
return [
84-
("ContractId", dht.int32, lambda contract: contract.conId),
84+
("ContractId", dht.int64, lambda contract: contract.conId),
8585
("SecId", dht.string, lambda contract: contract.secId),
8686
("SecIdType", dht.string, lambda contract: contract.secIdType),
8787
("SecType", dht.string, lambda contract: contract.secType),
@@ -130,8 +130,8 @@ def map_sec_id_list(value):
130130
("MinTick", dht.float64, lambda cd: cd.minTick),
131131
("OrderTypes", dht.stringset, lambda cd: to_string_set(cd.orderTypes.split(","))),
132132
("ValidExchanges", dht.stringset, lambda cd: to_string_set(cd.validExchanges.split(","))),
133-
("PriceMagnifier", dht.int32, lambda cd: cd.priceMagnifier),
134-
("UnderConId", dht.int32, lambda cd: cd.underConId),
133+
("PriceMagnifier", dht.int64, lambda cd: cd.priceMagnifier),
134+
("UnderConId", dht.int64, lambda cd: cd.underConId),
135135
("LongName", dht.string, lambda cd: cd.longName),
136136
("ContractMonth", dht.string, lambda cd: cd.contractMonth),
137137
("Industry", dht.string, lambda cd: cd.industry),
@@ -141,9 +141,9 @@ def map_sec_id_list(value):
141141
("TradingHours", dht.stringset, lambda cd: to_string_set(cd.tradingHours.split(";"))),
142142
("LiquidHours", dht.stringset, lambda cd: to_string_set(cd.liquidHours.split(";"))),
143143
("EvRule", dht.string, lambda cd: cd.evRule),
144-
("EvMultiplier", dht.int32, lambda cd: cd.evMultiplier),
145-
("MdSizeMultiplier", dht.int32, lambda cd: cd.mdSizeMultiplier),
146-
("AggGroup", dht.int32, lambda cd: map_null_int(cd.aggGroup)),
144+
("EvMultiplier", dht.int64, lambda cd: cd.evMultiplier),
145+
("MdSizeMultiplier", dht.int64, lambda cd: cd.mdSizeMultiplier),
146+
("AggGroup", dht.int64, lambda cd: map_null_int(cd.aggGroup)),
147147
("UnderSymbol", dht.string, lambda cd: cd.underSymbol),
148148
("UnderSecType", dht.string, lambda cd: cd.underSecType),
149149
("MarketRuleIds", dht.stringset, lambda cd: to_string_set(cd.marketRuleIds.split(","))),
@@ -159,7 +159,7 @@ def map_sec_id_list(value):
159159
("CouponType", dht.string, lambda cd: cd.couponType),
160160
("Callable", dht.bool_, lambda cd: cd.callable),
161161
("Putable", dht.bool_, lambda cd: cd.putable),
162-
("Coupon", dht.int32, lambda cd: cd.coupon),
162+
("Coupon", dht.int64, lambda cd: cd.coupon),
163163
("Convertible", dht.bool_, lambda cd: cd.convertible),
164164
("Maturity", dht.string, lambda cd: cd.maturity),
165165
# TODO: convert date time? Values are not provided in TWS, and the format is not documented. (https://github.com/deephaven-examples/deephaven-ib/issues/10)
@@ -207,8 +207,8 @@ def map_null(val):
207207
("High", dht.float64, lambda bd: bd.high),
208208
("Low", dht.float64, lambda bd: bd.low),
209209
("Close", dht.float64, lambda bd: bd.close),
210-
("Volume", dht.int32, lambda bd: map_null(bd.volume)),
211-
("BarCount", dht.int32, lambda bd: map_null(bd.barCount)),
210+
("Volume", dht.int64, lambda bd: map_null(bd.volume)),
211+
("BarCount", dht.int64, lambda bd: map_null(bd.barCount)),
212212
("Average", dht.float64, lambda bd: map_null(bd.average)),
213213
]
214214

@@ -234,9 +234,9 @@ def map_null(val):
234234
("High", dht.float64, lambda bd: bd.high),
235235
("Low", dht.float64, lambda bd: bd.low),
236236
("Close", dht.float64, lambda bd: bd.close),
237-
("Volume", dht.int32, lambda bd: map_null(bd.volume)),
237+
("Volume", dht.int64, lambda bd: map_null(bd.volume)),
238238
("WAP", dht.float64, lambda bd: map_null(bd.wap)),
239-
("Count", dht.int32, lambda bd: map_null(bd.count)),
239+
("Count", dht.int64, lambda bd: map_null(bd.count)),
240240
]
241241

242242

@@ -319,7 +319,7 @@ def map_special_conditions(special_conditions: str) -> Any:
319319
return [
320320
("Timestamp", dht.datetime, lambda t: unix_sec_to_dh_datetime(t.time)),
321321
("Price", dht.float64, lambda t: t.price),
322-
("Size", dht.int32, lambda t: t.size),
322+
("Size", dht.int64, lambda t: t.size),
323323
*_include_details(_details_tick_attrib_last(), lambda t: t.tickAttribLast),
324324
("Exchange", dht.string, lambda t: t.exchange),
325325
("SpecialConditions", dht.stringset, lambda t: map_special_conditions(t.specialConditions))
@@ -352,8 +352,8 @@ def _details_historical_tick_bid_ask() -> List[Tuple]:
352352
("Timestamp", dht.datetime, lambda t: unix_sec_to_dh_datetime(t.time)),
353353
("BidPrice", dht.float64, lambda t: t.priceBid),
354354
("AskPrice", dht.float64, lambda t: t.priceAsk),
355-
("BidSize", dht.int32, lambda t: t.sizeBid),
356-
("AskSize", dht.int32, lambda t: t.sizeAsk),
355+
("BidSize", dht.int64, lambda t: t.sizeBid),
356+
("AskSize", dht.int64, lambda t: t.sizeAsk),
357357
*_include_details(_details_tick_attrib_bid_ask(), lambda t: t.tickAttribBidAsk),
358358
]
359359

@@ -384,9 +384,9 @@ def _details_order() -> List[Tuple]:
384384
return [
385385

386386
# order identifier
387-
("OrderId", dht.int32, lambda o: o.orderId),
388-
("ClientId", dht.int32, lambda o: o.clientId),
389-
("PermId", dht.int32, lambda o: o.permId),
387+
("OrderId", dht.int64, lambda o: o.orderId),
388+
("ClientId", dht.int64, lambda o: o.clientId),
389+
("PermId", dht.int64, lambda o: o.permId),
390390

391391
# main order fields
392392
("Action", dht.string, lambda o: o.action),
@@ -403,18 +403,18 @@ def _details_order() -> List[Tuple]:
403403
("OcaType", dht.string, lambda o: map_values(o.ocaType, oca_types)),
404404
("OrderRef", dht.string, lambda o: o.orderRef),
405405
("Transmit", dht.bool_, lambda o: o.transmit),
406-
("ParentId", dht.int32, lambda o: o.parentId),
406+
("ParentId", dht.int64, lambda o: o.parentId),
407407
("BlockOrder", dht.bool_, lambda o: o.blockOrder),
408408
("SweepToFill", dht.bool_, lambda o: o.sweepToFill),
409-
("DisplaySize", dht.int32, lambda o: o.displaySize),
409+
("DisplaySize", dht.int64, lambda o: o.displaySize),
410410
("TriggerMethod", dht.string, lambda o: map_values(o.triggerMethod, trigger_methods)),
411411
("OutsideRth", dht.bool_, lambda o: o.outsideRth),
412412
("Hidden", dht.bool_, lambda o: o.hidden),
413413
("GoodAfterTime", dht.string, lambda o: o.goodAfterTime),
414414
("GoodTillDate", dht.string, lambda o: o.goodTillDate),
415415
("Rule80A", dht.string, lambda o: map_values(o.rule80A, rule80_values)),
416416
("AllOrNone", dht.bool_, lambda o: o.allOrNone),
417-
("MinQty", dht.int32, lambda o: o.minQty),
417+
("MinQty", dht.int64, lambda o: o.minQty),
418418
("PercentOffset", dht.float64, lambda o: o.percentOffset),
419419
("OverridePercentageConstraints", dht.bool_, lambda o: o.overridePercentageConstraints),
420420
("TrailStopPrice", dht.float64, lambda o: o.trailStopPrice),
@@ -431,7 +431,7 @@ def _details_order() -> List[Tuple]:
431431
("OpenClose", dht.string, lambda o: map_values(o.openClose, open_close_values)),
432432
("Origin", dht.string, lambda o: map_values(o.origin, origin_values)),
433433
("ShortSaleSlot", dht.string, lambda o: map_values(o.shortSaleSlot, short_sale_slot_values)),
434-
("ExemptCode", dht.int32, lambda o: o.exemptCode),
434+
("ExemptCode", dht.int64, lambda o: o.exemptCode),
435435

436436
# SMART routing only
437437
("DiscretionaryAmt", dht.float64, lambda o: o.discretionaryAmt),
@@ -458,31 +458,31 @@ def _details_order() -> List[Tuple]:
458458
("VolatilityType", dht.string, lambda o: map_values(o.volatilityType, volatility_type)),
459459
("DeltaNeutralOrderType", dht.string, lambda o: o.deltaNeutralOrderType),
460460
("DeltaNeutralAuxPrice", dht.float64, lambda o: o.deltaNeutralAuxPrice),
461-
("DeltaNeutralConId", dht.int32, lambda o: o.deltaNeutralConId),
461+
("DeltaNeutralConId", dht.int64, lambda o: o.deltaNeutralConId),
462462
("DeltaNeutralSettlingFirm", dht.string, lambda o: o.deltaNeutralSettlingFirm),
463463
("DeltaNeutralClearingAccount", dht.string, lambda o: o.deltaNeutralClearingAccount),
464464
("DeltaNeutralClearingIntent", dht.string, lambda o: o.deltaNeutralClearingIntent),
465465
("DeltaNeutralOpenClose", dht.string, lambda o: o.deltaNeutralOpenClose),
466466
("DeltaNeutralShortSale", dht.bool_, lambda o: o.deltaNeutralShortSale),
467-
("DeltaNeutralShortSaleSlot", dht.int32, lambda o: o.deltaNeutralShortSaleSlot),
467+
("DeltaNeutralShortSaleSlot", dht.int64, lambda o: o.deltaNeutralShortSaleSlot),
468468
("DeltaNeutralDesignatedLocation", dht.string, lambda o: o.deltaNeutralDesignatedLocation),
469469
("ContinuousUpdate", dht.bool_, lambda o: o.continuousUpdate),
470470
("ReferencePriceType", dht.string, lambda o: map_values(o.referencePriceType, reference_price_type)),
471471

472472
# COMBO ORDERS ONLY
473473
("BasisPoints", dht.float64, lambda o: o.basisPoints),
474-
("BasisPointsType", dht.int32, lambda o: o.basisPointsType),
474+
("BasisPointsType", dht.int64, lambda o: o.basisPointsType),
475475

476476
# SCALE ORDERS ONLY
477-
("ScaleInitLevelSize", dht.int32, lambda o: o.scaleInitLevelSize),
478-
("ScaleSubsLevelSize", dht.int32, lambda o: o.scaleSubsLevelSize),
477+
("ScaleInitLevelSize", dht.int64, lambda o: o.scaleInitLevelSize),
478+
("ScaleSubsLevelSize", dht.int64, lambda o: o.scaleSubsLevelSize),
479479
("ScalePriceIncrement", dht.float64, lambda o: o.scalePriceIncrement),
480480
("ScalePriceAdjustValue", dht.float64, lambda o: o.scalePriceAdjustValue),
481-
("ScalePriceAdjustInterval", dht.int32, lambda o: o.scalePriceAdjustInterval),
481+
("ScalePriceAdjustInterval", dht.int64, lambda o: o.scalePriceAdjustInterval),
482482
("ScaleProfitOffset", dht.float64, lambda o: o.scaleProfitOffset),
483483
("ScaleAutoReset", dht.bool_, lambda o: o.scaleAutoReset),
484-
("ScaleInitPosition", dht.int32, lambda o: o.scaleInitPosition),
485-
("ScaleInitFillQty", dht.int32, lambda o: o.scaleInitFillQty),
484+
("ScaleInitPosition", dht.int64, lambda o: o.scaleInitPosition),
485+
("ScaleInitFillQty", dht.int64, lambda o: o.scaleInitFillQty),
486486
("ScaleRandomPercent", dht.bool_, lambda o: o.scaleRandomPercent),
487487
("ScaleTable", dht.string, lambda o: o.scaleTable),
488488

@@ -521,7 +521,7 @@ def _details_order() -> List[Tuple]:
521521
("OrderMiscOptions", dht.stringset, lambda o: to_string_set(o.orderMiscOptions)),
522522

523523
# VER PEG2BENCH fields:
524-
("ReferenceContractId", dht.int32, lambda o: o.referenceContractId),
524+
("ReferenceContractId", dht.int64, lambda o: o.referenceContractId),
525525
("PeggedChangeAmount", dht.float64, lambda o: o.peggedChangeAmount),
526526
("IsPeggedChangeAmountDecrease", dht.bool_, lambda o: o.isPeggedChangeAmountDecrease),
527527
("ReferenceChangeAmount", dht.float64, lambda o: o.referenceChangeAmount),
@@ -532,7 +532,7 @@ def _details_order() -> List[Tuple]:
532532
("AdjustedStopPrice", dht.float64, lambda o: o.adjustedStopPrice),
533533
("AdjustedStopLimitPrice", dht.float64, lambda o: o.adjustedStopLimitPrice),
534534
("AdjustedTrailingAmount", dht.float64, lambda o: o.adjustedTrailingAmount),
535-
("AdjustableTrailingUnit", dht.int32, lambda o: o.adjustableTrailingUnit),
535+
("AdjustableTrailingUnit", dht.int64, lambda o: o.adjustableTrailingUnit),
536536
("LmtPriceOffset", dht.float64, lambda o: o.lmtPriceOffset),
537537

538538
("Conditions", dht.stringset, lambda o: to_string_set(o.conditions)),
@@ -558,12 +558,12 @@ def _details_order() -> List[Tuple]:
558558

559559
("AutoCancelDate", dht.string, lambda o: o.autoCancelDate),
560560
("FilledQuantity", dht.float64, lambda o: o.filledQuantity),
561-
("RefFuturesConId", dht.int32, lambda o: o.refFuturesConId),
561+
("RefFuturesConId", dht.int64, lambda o: o.refFuturesConId),
562562
("AutoCancelParent", dht.bool_, lambda o: o.autoCancelParent),
563563
("Shareholder", dht.string, lambda o: o.shareholder),
564564
("ImbalanceOnly", dht.bool_, lambda o: o.imbalanceOnly),
565565
("RouteMarketableToBbo", dht.bool_, lambda o: o.routeMarketableToBbo),
566-
("ParentPermId", dht.int32, lambda o: o.parentPermId),
566+
("ParentPermId", dht.int64, lambda o: o.parentPermId),
567567

568568
("UsePriceMgmtAlgo", dht.bool_, lambda o: o.usePriceMgmtAlgo),
569569

@@ -619,17 +619,17 @@ def _details_execution() -> List[Tuple]:
619619
("Side", dht.string, lambda e: e.side),
620620
("Shares", dht.float64, lambda e: e.shares),
621621
("Price", dht.float64, lambda e: e.price),
622-
("PermId", dht.int32, lambda e: e.permId),
623-
("ClientId", dht.int32, lambda e: e.clientId),
624-
("OrderId", dht.int32, lambda e: e.orderId),
625-
("Liquidation", dht.int32, lambda e: e.liquidation),
622+
("PermId", dht.int64, lambda e: e.permId),
623+
("ClientId", dht.int64, lambda e: e.clientId),
624+
("OrderId", dht.int64, lambda e: e.orderId),
625+
("Liquidation", dht.int64, lambda e: e.liquidation),
626626
("CumQty", dht.float64, lambda e: e.cumQty),
627627
("AvgPrice", dht.float64, lambda e: e.avgPrice),
628628
("OrderRef", dht.string, lambda e: e.orderRef),
629629
("EvRule", dht.string, lambda e: e.evRule),
630630
("EvMultiplier", dht.float64, lambda e: e.evMultiplier),
631631
("ModelCode", dht.string, lambda e: e.modelCode),
632-
("LastLiquidity", dht.int32, lambda e: e.lastLiquidity),
632+
("LastLiquidity", dht.int64, lambda e: e.lastLiquidity),
633633
]
634634

635635

0 commit comments

Comments
 (0)