feat libs,sdk,trend
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -13,118 +13,75 @@ def _number(value: Any, kind: type = float) -> Any:
|
||||
|
||||
@dataclass(slots=True)
|
||||
class OrderItem:
|
||||
"""由 QMT 委托明细解析得到的标准订单记录。"""
|
||||
"""Fields match format_orders() exactly."""
|
||||
|
||||
id: str
|
||||
code: str
|
||||
side: str
|
||||
remark: str
|
||||
status: str
|
||||
created_at: datetime | None
|
||||
volume: int
|
||||
local_order_id: str = ""
|
||||
traded_volume: int = 0
|
||||
remaining_volume: int = 0
|
||||
exchange_id: str = ""
|
||||
name: str = ""
|
||||
price: float = 0.0
|
||||
trade_price: float = 0.0
|
||||
stock_code: str = ""
|
||||
order_sys_id: str = ""
|
||||
ref: int = 0
|
||||
order_ref: str = ""
|
||||
direction: int = 0
|
||||
offset_flag: int = 0
|
||||
limit_price: float = 0.0
|
||||
volume_total_original: int = 0
|
||||
volume_traded: int = 0
|
||||
volume_total: int = 0
|
||||
traded_price: float = 0.0
|
||||
trade_amount: float = 0.0
|
||||
insert_date: str = ""
|
||||
insert_time: str = ""
|
||||
remark: str = ""
|
||||
order_status: int = 0
|
||||
|
||||
@classmethod
|
||||
def from_trade_detail(cls, data: dict[str, Any]) -> "OrderItem":
|
||||
"""从 TradeDetailData 的 QMT 原始字段创建订单。"""
|
||||
instrument_id = str(data.get("m_strInstrumentID") or "")
|
||||
exchange_id = str(data.get("m_strExchangeID") or "")
|
||||
code = (
|
||||
f"{instrument_id}.{exchange_id}"
|
||||
if instrument_id and exchange_id
|
||||
else instrument_id
|
||||
)
|
||||
remaining_volume = _number(data.get("m_nVolumeTotal"), int)
|
||||
traded_volume = _number(data.get("m_nVolumeTraded"), int)
|
||||
remark = str(data.get("m_strRemark") or "")
|
||||
return cls(
|
||||
id=str(data.get("m_strOrderSysID") or ""),
|
||||
code=code,
|
||||
side={"23": "BUY", "24": "SELL", "48": "BUY", "49": "SELL"}.get(
|
||||
str(data.get("m_nOffsetFlag")), ""
|
||||
),
|
||||
remark=remark,
|
||||
status=str(data.get("m_nOrderStatus") or ""),
|
||||
created_at=_trade_datetime(data),
|
||||
volume=remaining_volume + traded_volume,
|
||||
local_order_id=remark.split("|", 1)[0] if remark else "",
|
||||
traded_volume=traded_volume,
|
||||
remaining_volume=remaining_volume,
|
||||
exchange_id=exchange_id,
|
||||
name=str(data.get("m_strInstrumentName") or ""),
|
||||
price=_number(data.get("m_dPrice")),
|
||||
trade_price=_number(data.get("m_dTradePrice")),
|
||||
trade_amount=_number(data.get("m_dTradeAmount")),
|
||||
)
|
||||
@property
|
||||
def side(self) -> str:
|
||||
return _side(self.offset_flag)
|
||||
|
||||
@property
|
||||
def local_order_id(self) -> str:
|
||||
return self.remark.split("|", 1)[0]
|
||||
|
||||
@property
|
||||
def created_at(self) -> datetime | None:
|
||||
return _parse_datetime(self.insert_date, self.insert_time)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class DealItem:
|
||||
"""Execution data from the API's fixed order-detail fields."""
|
||||
"""Fields match format_deals() exactly."""
|
||||
|
||||
sys_order_id: str = ""
|
||||
local_order_id: str = ""
|
||||
code: str = ""
|
||||
instrument_id: str = ""
|
||||
exchange_id: str = ""
|
||||
name: str = ""
|
||||
offset_flag: str = ""
|
||||
side: str = ""
|
||||
status: str = ""
|
||||
remaining_volume: int = 0
|
||||
traded_volume: int = 0
|
||||
order_time: int = 0
|
||||
insert_date: str = ""
|
||||
insert_time: str = ""
|
||||
remark: str = ""
|
||||
stock_code: str = ""
|
||||
order_sys_id: str = ""
|
||||
ref: int = 0
|
||||
order_ref: str = ""
|
||||
direction: int = 0
|
||||
offset_flag: int = 0
|
||||
price: float = 0.0
|
||||
trade_price: float = 0.0
|
||||
volume: int = 0
|
||||
trade_amount: float = 0.0
|
||||
trade_date: str = ""
|
||||
trade_time: str = ""
|
||||
remark: str = ""
|
||||
close_profit: float = 0.0
|
||||
|
||||
@classmethod
|
||||
def from_trade_detail(cls, data: dict[str, Any]) -> "DealItem":
|
||||
instrument_id = str(data.get("m_strInstrumentID") or "")
|
||||
exchange_id = str(data.get("m_strExchangeID") or "")
|
||||
remark = str(data.get("m_strRemark") or "")
|
||||
offset_flag = str(data.get("m_nOffsetFlag") or "")
|
||||
return cls(
|
||||
sys_order_id=str(data.get("m_strOrderSysID") or ""),
|
||||
local_order_id=remark.split("|", 1)[0] if remark else "",
|
||||
code=f"{instrument_id}.{exchange_id}" if instrument_id and exchange_id else instrument_id,
|
||||
instrument_id=instrument_id,
|
||||
exchange_id=exchange_id,
|
||||
name=str(data.get("m_strInstrumentName") or ""),
|
||||
offset_flag=offset_flag,
|
||||
side={"23": "BUY", "24": "SELL", "48": "BUY", "49": "SELL"}.get(offset_flag, ""),
|
||||
status=str(data.get("m_nOrderStatus") or ""),
|
||||
remaining_volume=_number(data.get("m_nVolumeTotal"), int),
|
||||
traded_volume=_number(data.get("m_nVolumeTraded"), int),
|
||||
order_time=_number(data.get("m_nOrderTime"), int),
|
||||
insert_date=str(data.get("m_strInsertDate") or ""),
|
||||
insert_time=str(data.get("m_strInsertTime") or ""),
|
||||
remark=remark,
|
||||
price=_number(data.get("m_dPrice")),
|
||||
trade_price=_number(data.get("m_dTradePrice")),
|
||||
trade_amount=_number(data.get("m_dTradeAmount")),
|
||||
)
|
||||
@property
|
||||
def side(self) -> str:
|
||||
return _side(self.offset_flag)
|
||||
|
||||
@property
|
||||
def local_order_id(self) -> str:
|
||||
return self.remark.split("|", 1)[0]
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class PositionItem:
|
||||
"""Fields match format_holding() exactly."""
|
||||
|
||||
stock_code: str = ""
|
||||
stock_name: str = ""
|
||||
trade_id: str = ""
|
||||
direction: Any = None
|
||||
volume: int = 0
|
||||
open_cost: float = 0.0
|
||||
open_price: float = 0.0
|
||||
open_cost: float = 0.0
|
||||
float_profit: float = 0.0
|
||||
market_value: float = 0.0
|
||||
stock_holder: str = ""
|
||||
@@ -137,67 +94,14 @@ class PositionItem:
|
||||
future_trade_type: Any = None
|
||||
expire_date: str = ""
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict[str, Any], code: str = "") -> "PositionItem":
|
||||
return cls(
|
||||
stock_code=str(data.get("StockCode") or code),
|
||||
stock_name=str(data.get("StockName") or ""),
|
||||
trade_id=str(data.get("TradeID") or ""),
|
||||
open_cost=_number(data.get("OpenCost")),
|
||||
direction=data.get("Direction"),
|
||||
volume=_number(data.get("Volume"), int),
|
||||
open_price=_number(data.get("OpenPrice")),
|
||||
float_profit=_number(data.get("FloatProfit")),
|
||||
market_value=_number(data.get("MarketValue")),
|
||||
stock_holder=str(data.get("StockHolder") or ""),
|
||||
frozen_volume=_number(data.get("FrozenVolume"), int),
|
||||
can_use_volume=_number(data.get("CanUseVolume"), int),
|
||||
on_road_volume=_number(data.get("OnRoadVolume"), int),
|
||||
yesterday_volume=_number(data.get("YesterdayVolume"), int),
|
||||
last_price=_number(data.get("LastPrice")),
|
||||
profit_rate=_number(data.get("ProfitRate")),
|
||||
future_trade_type=data.get("FutureTradeType"),
|
||||
expire_date=str(data.get("ExpireDate") or ""),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_trade_detail(cls, data: dict[str, Any]) -> "PositionItem":
|
||||
"""从 TradeDetailData/Holding 的 QMT 原始字段创建持仓。"""
|
||||
return cls(
|
||||
stock_code=str(data.get("StockCode") or ""),
|
||||
stock_name=str(data.get("StockName") or ""),
|
||||
trade_id=str(data.get("TradeID") or ""),
|
||||
direction=data.get("Direction"),
|
||||
volume=_number(data.get("Volume"), int),
|
||||
open_cost=_number(data.get("OpenCost")),
|
||||
open_price=_number(data.get("OpenPrice")),
|
||||
float_profit=_number(data.get("FloatProfit")),
|
||||
market_value=_number(data.get("MarketValue")),
|
||||
stock_holder=str(data.get("StockHolder") or ""),
|
||||
frozen_volume=_number(data.get("FrozenVolume"), int),
|
||||
can_use_volume=_number(data.get("CanUseVolume"), int),
|
||||
on_road_volume=_number(data.get("OnRoadVolume"), int),
|
||||
yesterday_volume=_number(data.get("YesterdayVolume"), int),
|
||||
last_price=_number(data.get("LastPrice")),
|
||||
profit_rate=_number(data.get("ProfitRate")),
|
||||
future_trade_type=data.get("FutureTradeType"),
|
||||
expire_date=str(data.get("ExpireDate") or ""),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Assets:
|
||||
"""Fields match format_assets() exactly."""
|
||||
|
||||
total: float = 0.0
|
||||
available: float = 0.0
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, data: dict[str, Any]) -> "Assets":
|
||||
"""兼容新版 QMT 原始资金字段及旧版简化字段。"""
|
||||
return cls(
|
||||
total=_number(data.get("m_dBalance", data.get("total"))),
|
||||
available=_number(data.get("m_dAvailable", data.get("available"))),
|
||||
)
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Portfolio:
|
||||
@@ -206,17 +110,14 @@ class Portfolio:
|
||||
orders: list[OrderItem]
|
||||
|
||||
|
||||
def _trade_datetime(data: dict[str, Any]) -> datetime | None:
|
||||
return _parse_datetime(
|
||||
str(data.get("m_strInsertDate") or ""),
|
||||
str(data.get("m_strInsertTime") or ""),
|
||||
)
|
||||
def _side(offset_flag: int) -> str:
|
||||
return {"23": "BUY", "24": "SELL", "48": "BUY", "49": "SELL"}.get(str(offset_flag), "")
|
||||
|
||||
|
||||
def _parse_datetime(date: str, clock: str) -> datetime | None:
|
||||
clock = clock.replace(":", "").zfill(6)
|
||||
try:
|
||||
return datetime.strptime(date + clock, "%Y%m%d%H%M%S")
|
||||
return datetime.strptime(date.replace("-", "") + clock, "%Y%m%d%H%M%S")
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
@@ -17,33 +17,33 @@ class PortfolioMixin:
|
||||
def portfolio(self) -> Portfolio:
|
||||
data = self._get_json("/api/portfolio") or {}
|
||||
positions = {
|
||||
code: PositionItem.from_dict(value, code)
|
||||
code: PositionItem(**value)
|
||||
for code, value in data.get("positions", {}).items()
|
||||
}
|
||||
return Portfolio(
|
||||
assets=Assets.from_dict(data.get("assets", {})),
|
||||
assets=Assets(**data.get("assets", {})),
|
||||
positions=positions,
|
||||
orders=[OrderItem.from_trade_detail(row) for row in data.get("orders", [])],
|
||||
orders=[OrderItem(**row) for row in data.get("orders", [])],
|
||||
)
|
||||
|
||||
def positions(self) -> tuple[list[str], list[PositionItem]]:
|
||||
data = self._get_json("/api/portfolio/positions") or {}
|
||||
positions = [
|
||||
PositionItem.from_dict(value, code)
|
||||
for code, value in data.get("data", {}).items()
|
||||
PositionItem(**value)
|
||||
for value in data.values()
|
||||
]
|
||||
return [item.stock_code for item in positions], positions
|
||||
return list(data), positions
|
||||
|
||||
def assets(self) -> Assets:
|
||||
return Assets.from_dict(self._get_json("/api/portfolio/assets") or {})
|
||||
return Assets(**(self._get_json("/api/portfolio/assets") or {}))
|
||||
|
||||
def orders(self) -> list[OrderItem]:
|
||||
data = self._get_json("/api/portfolio/order") or []
|
||||
return [OrderItem.from_trade_detail(row) for row in data]
|
||||
return [OrderItem(**row) for row in data]
|
||||
|
||||
def deals(self) -> list[DealItem]:
|
||||
data = self._get_json("/api/portfolio/deal") or []
|
||||
return [DealItem.from_trade_detail(row) for row in data]
|
||||
return [DealItem(**row) for row in data]
|
||||
|
||||
def trade_detail_data(self, datatype: str) -> Any:
|
||||
datatype = str(datatype).strip().lower()
|
||||
|
||||
Reference in New Issue
Block a user