vnpy_okx 2025.12.22__tar.gz → 2026.1.11__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vnpy_okx
3
- Version: 2025.12.22
3
+ Version: 2026.1.11
4
4
  Summary: OKX trading gateway for VeighNa.
5
5
  Project-URL: Homepage, https://www.github.com/veighna-global
6
6
  Project-URL: Source, https://www.github.com/veighna-global
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
33
33
  </p>
34
34
 
35
35
  <p align="center">
36
- <img src ="https://img.shields.io/badge/version-2025.12.22-blueviolet.svg"/>
36
+ <img src ="https://img.shields.io/badge/version-2025.12.28-blueviolet.svg"/>
37
37
  <img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
38
38
  <img src ="https://img.shields.io/badge/python-3.10|3.11|3.12|3.13-blue.svg" />
39
39
  <img src ="https://img.shields.io/github/license/veighna-global/vnpy_okx.svg?color=orange"/>
@@ -5,7 +5,7 @@
5
5
  </p>
6
6
 
7
7
  <p align="center">
8
- <img src ="https://img.shields.io/badge/version-2025.12.22-blueviolet.svg"/>
8
+ <img src ="https://img.shields.io/badge/version-2025.12.28-blueviolet.svg"/>
9
9
  <img src ="https://img.shields.io/badge/platform-windows|linux|macos-yellow.svg"/>
10
10
  <img src ="https://img.shields.io/badge/python-3.10|3.11|3.12|3.13-blue.svg" />
11
11
  <img src ="https://img.shields.io/github/license/veighna-global/vnpy_okx.svg?color=orange"/>
@@ -23,7 +23,7 @@
23
23
  from .okx_gateway import OkxGateway
24
24
 
25
25
 
26
- __version__ = "2025.12.22"
26
+ __version__ = "2026.01.11"
27
27
 
28
28
 
29
29
  __all__ = ["OkxGateway"]
@@ -10,6 +10,7 @@ from types import TracebackType
10
10
  from collections.abc import Callable
11
11
  from time import sleep
12
12
  from decimal import Decimal
13
+ from typing import cast
13
14
 
14
15
  from vnpy.event import EventEngine, Event, EVENT_TIMER
15
16
  from vnpy.trader.constant import (
@@ -116,7 +117,7 @@ class OkxGateway(BaseGateway):
116
117
  "Margin Currency": ""
117
118
  }
118
119
 
119
- exchanges: Exchange = [Exchange.GLOBAL]
120
+ exchanges: list[Exchange] = [Exchange.GLOBAL]
120
121
 
121
122
  def __init__(self, event_engine: EventEngine, gateway_name: str) -> None:
122
123
  """
@@ -339,7 +340,7 @@ class OkxGateway(BaseGateway):
339
340
  self.orders[order.orderid] = order
340
341
  super().on_order(order)
341
342
 
342
- def get_order(self, orderid: str) -> OrderData:
343
+ def get_order(self, orderid: str) -> OrderData | None:
343
344
  """
344
345
  Get previously saved order by order id.
345
346
 
@@ -401,7 +402,7 @@ class OkxGateway(BaseGateway):
401
402
  Returns:
402
403
  OrderData: VeighNa order object
403
404
  """
404
- contract: ContractData = self.get_contract_by_name(data["instId"])
405
+ contract: ContractData = cast(ContractData, self.get_contract_by_name(data["instId"]))
405
406
 
406
407
  order_id: str = data["clOrdId"]
407
408
  if order_id:
@@ -439,7 +440,7 @@ class OkxGateway(BaseGateway):
439
440
  Returns:
440
441
  OrderData: VeighNa order object
441
442
  """
442
- contract: ContractData = self.get_contract_by_name(data["sprdId"])
443
+ contract: ContractData = cast(ContractData, self.get_contract_by_name(data["sprdId"]))
443
444
 
444
445
  order_id: str = data["clOrdId"]
445
446
  if order_id:
@@ -764,6 +765,7 @@ class RestApi(RestClient):
764
765
  net_position=net_position,
765
766
  gateway_name=self.gateway_name,
766
767
  )
768
+ contract.extra = d
767
769
 
768
770
  self.gateway.on_contract(contract)
769
771
 
@@ -795,7 +797,7 @@ class RestApi(RestClient):
795
797
  leg_symbols: list[str] = []
796
798
  for leg in d["legs"]:
797
799
  leg_name: str = leg["instId"]
798
- leg_contract: ContractData = self.gateway.get_contract_by_name(leg_name)
800
+ leg_contract: ContractData = cast(ContractData, self.gateway.get_contract_by_name(leg_name))
799
801
  leg_symbols.append(leg_contract.symbol)
800
802
 
801
803
  contract: ContractData = ContractData(
@@ -877,6 +879,11 @@ class RestApi(RestClient):
877
879
  self.gateway.write_log(f"Query kline history failed, symbol not found: {req.symbol}")
878
880
  return []
879
881
 
882
+ # Validate interval is not None
883
+ if not req.interval:
884
+ self.gateway.write_log(f"Query kline history failed, interval not found: {req.symbol}")
885
+ return []
886
+
880
887
  # Initialize buffer for storing bars
881
888
  buf: dict[datetime, BarData] = {}
882
889
  limit: str = "100"
@@ -913,7 +920,7 @@ class RestApi(RestClient):
913
920
  break
914
921
  else:
915
922
  data: dict = resp.json()
916
- bar_data: list = data.get("data", None)
923
+ bar_data: list | None = data.get("data", None)
917
924
 
918
925
  if not bar_data:
919
926
  msg: str = data.get("msg", "No data returned.")
@@ -1395,7 +1402,7 @@ class PrivateApi(WebsocketApi):
1395
1402
  # Process trade data for filled or partially filled orders
1396
1403
  # Round trade volume number to meet minimum volume precision
1397
1404
  trade_volume: float = float(d["fillSz"])
1398
- contract: ContractData = self.gateway.get_contract_by_symbol(order.symbol)
1405
+ contract: ContractData | None = self.gateway.get_contract_by_symbol(order.symbol)
1399
1406
  if contract:
1400
1407
  trade_volume = round_to(trade_volume, contract.min_volume)
1401
1408
 
@@ -1451,7 +1458,7 @@ class PrivateApi(WebsocketApi):
1451
1458
  data: list = packet["data"]
1452
1459
  for d in data:
1453
1460
  name: str = d["instId"]
1454
- contract: ContractData = self.gateway.get_contract_by_name(name)
1461
+ contract: ContractData = cast(ContractData, self.gateway.get_contract_by_name(name))
1455
1462
 
1456
1463
  pos: float = float(d["pos"])
1457
1464
  price: float = get_float_value(d, "avgPx")
@@ -1483,9 +1490,11 @@ class PrivateApi(WebsocketApi):
1483
1490
  # Wrong parameters
1484
1491
  if packet["code"] != "0":
1485
1492
  if not data:
1486
- order: OrderData = self.reqid_order_map[packet["id"]]
1487
- order.status = Status.REJECTED
1488
- self.gateway.on_order(order)
1493
+ order: OrderData | None = self.reqid_order_map.get(packet["id"], None)
1494
+ if order:
1495
+ order.status = Status.REJECTED
1496
+ self.gateway.on_order(order)
1497
+
1489
1498
  return
1490
1499
 
1491
1500
  # Failed to process
@@ -1516,10 +1525,11 @@ class PrivateApi(WebsocketApi):
1516
1525
  """
1517
1526
  # Wrong parameters
1518
1527
  if packet["code"] != "0":
1519
- code: str = packet["code"]
1520
1528
  msg: str = packet["msg"]
1521
- self.gateway.write_log(f"Cancel order failed, status code: {code}, message: {msg}")
1522
- return
1529
+ if msg:
1530
+ code: str = packet["code"]
1531
+ self.gateway.write_log(f"Cancel order failed, status code: {code}, message: {msg}")
1532
+ return
1523
1533
 
1524
1534
  # Failed to process
1525
1535
  data: list = packet["data"]
@@ -1624,9 +1634,15 @@ class PrivateApi(WebsocketApi):
1624
1634
  "tdMode": "cross" # Only support cross margin mode
1625
1635
  }
1626
1636
 
1637
+ # Add extra field for portfolio margin
1627
1638
  if self.margin_currency:
1628
1639
  arg["ccy"] = self.margin_currency
1629
1640
 
1641
+ # Add extra field for spot
1642
+ if "SPOT" in req.symbol:
1643
+ quote_ccy_list: list[str] = contract.extra["tradeQuoteCcyList"] # type: ignore
1644
+ arg["tradeQuoteCcy"] = quote_ccy_list[0]
1645
+
1630
1646
  # Create websocket request with unique request ID
1631
1647
  self.reqid += 1
1632
1648
  packet: dict = {
@@ -1660,7 +1676,7 @@ class PrivateApi(WebsocketApi):
1660
1676
  return
1661
1677
 
1662
1678
  # Initialize cancel parameters
1663
- arg: dict = {}
1679
+ arg: dict = {"instId": contract.name}
1664
1680
 
1665
1681
  # Determine the type of order ID to use for cancellation
1666
1682
  # OKX supports both client order ID and exchange order ID for cancellation
@@ -1954,10 +1970,11 @@ class BusinessApi(WebsocketApi):
1954
1970
  """
1955
1971
  # Wrong parameters
1956
1972
  if packet["code"] != "0":
1957
- code: str = packet["code"]
1958
1973
  msg: str = packet["msg"]
1959
- self.gateway.write_log(f"Cancel order failed, status code: {code}, message: {msg}")
1960
- return
1974
+ if msg:
1975
+ code: str = packet["code"]
1976
+ self.gateway.write_log(f"Cancel order failed, status code: {code}, message: {msg}")
1977
+ return
1961
1978
 
1962
1979
  # Failed to process
1963
1980
  data: list = packet["data"]
File without changes
File without changes