Skip to content

Commit

Permalink
0.9.55 update
Browse files Browse the repository at this point in the history
  • Loading branch information
zengbin93 committed Jul 9, 2024
1 parent fcbb50f commit 4eb79f7
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 36 deletions.
2 changes: 1 addition & 1 deletion czsc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@
__version__ = "0.9.55"
__author__ = "zengbin93"
__email__ = "[email protected]"
__date__ = "20240616"
__date__ = "20240706"


def welcome():
Expand Down
3 changes: 1 addition & 2 deletions czsc/connectors/cooperation.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,6 @@ def stocks_daily_klines(sdt="20170101", edt="20240101", **kwargs):
for year in years:
ttl = 3600 * 6 if year == str(datetime.now().year) else -1
kline = dc.pro_bar(trade_year=year, adj=adj, v=2, ttl=ttl)
kline["price"] = kline["open"].shift(-1)
kline["price"] = kline["price"].fillna(kline["close"])
res.append(kline)

dfk = pd.concat(res, ignore_index=True)
Expand All @@ -223,6 +221,7 @@ def stocks_daily_klines(sdt="20170101", edt="20240101", **kwargs):
dfk = dfk[~dfk["code"].str.endswith(".BJ")].reset_index(drop=True)

dfk = dfk.rename(columns={"code": "symbol"})
dfk["price"] = dfk["close"]
nxb = kwargs.get("nxb", [1, 2, 5, 10, 20, 30, 60])
if nxb:
dfk = czsc.update_nxb(dfk, nseq=nxb)
Expand Down
143 changes: 120 additions & 23 deletions czsc/connectors/qmt_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,47 @@ def close_exe_window(title):
logger.error(f"没有找到 {title} 程序")


def wait_qmt_ready(timeout=60):
"""等待 QMT 窗口就绪"""
start = time.time()
while time.time() - start < timeout:
x = xtdata.get_full_tick(code_list=["000001.SZ"])
if x and x["000001.SZ"]["lastPrice"] > 0:
return True

logger.warning("等待 QMT 窗口就绪")
time.sleep(1)
return False


def initialize_qmt(**kwargs):
"""初始化 QMT 交易端
:param kwargs:
mini_qmt_dir: str, mini qmt 目录
account_id: str, 账户id
callback_params: dict, TraderCallback 回调类的参数
:return: xtt, acc
xtt - XtQuantTrader
acc - StockAccount
"""
import random

mini_qmt_dir = kwargs.get("mini_qmt_dir")
account_id = kwargs.get("account_id")

session = random.randint(10000, 20000)
callback = TraderCallback(**kwargs.get("callback_params", {}))
xtt = XtQuantTrader(mini_qmt_dir, session=session, callback=callback)
acc = StockAccount(account_id, "STOCK")
xtt.start()
xtt.connect()
assert xtt.connected, "交易服务器连接失败"
_res = xtt.subscribe(acc)
assert _res == 0, "账号订阅失败"
return xtt, acc


def start_qmt_exe(acc, pwd, qmt_exe, title, max_retry=6, **kwargs):
"""windows系统:启动 QMT,并登录
Expand Down Expand Up @@ -578,14 +619,53 @@ def cancel_timeout_orders(xtt: XtQuantTrader, acc: StockAccount, minutes=30):
xtt.cancel_order_stock(acc, o.order_id)


def is_order_exist(xtt: XtQuantTrader, acc: StockAccount, symbol, order_type, volume=None):
"""判断是否存在相同的委托单
http://docs.thinktrader.net/pages/ee0e9b/#%E8%82%A1%E7%A5%A8%E5%90%8C%E6%AD%A5%E6%92%A4%E5%8D%95
http://docs.thinktrader.net/pages/ee0e9b/#%E5%A7%94%E6%89%98%E6%9F%A5%E8%AF%A2
http://docs.thinktrader.net/pages/198696/#%E5%A7%94%E6%89%98xtorder
def query_stock_orders(xtt, acc, **kwargs):
"""查询股票委托单
:param xtt: XtQuantTrader, QMT 交易接口
:param acc: StockAccount, 账户
:param kwargs:
cancelable_only: bool, 是否只查询可撤单的委托单
start_time: str, 开始时间,格式:09:00:00
"""
cancelable_only = kwargs.get("cancelable_only", False)
orders = xtt.query_stock_orders(acc, cancelable_only)

start_time = kwargs.get("start_time", "09:00:00")
rows = []
for order in orders:
row = {
"account_id": order.account_id,
"stock_code": order.stock_code,
"order_id": order.order_id,
"order_sysid": order.order_sysid,
"order_time": order.order_time,
"order_type": order.order_type,
"order_volume": order.order_volume,
"price_type": order.price_type,
"price": order.price,
"traded_volume": order.traded_volume,
"traded_price": order.traded_price,
"order_status": order.order_status,
"status_msg": order.status_msg,
"strategy_name": order.strategy_name,
"order_remark": order.order_remark,
"direction": order.direction,
"offset_flag": order.offset_flag,
}
rows.append(row)

dfr = pd.DataFrame(rows)
dfr["order_time"] = pd.to_datetime(dfr["order_time"], unit="s") + pd.Timedelta(hours=8)
dfr["order_date"] = dfr["order_time"].dt.strftime("%Y-%m-%d")
dfr["order_time"] = dfr["order_time"].dt.strftime("%H:%M:%S")
if start_time:
dfr = dfr[dfr["order_time"] >= start_time].copy().reset_index(drop=True)
return dfr


def is_order_exist(xtt: XtQuantTrader, acc: StockAccount, symbol, order_type, volume=None):
"""判断是否存在相同方向的委托单"""
orders = xtt.query_stock_orders(acc, cancelable_only=False)
for o in orders:
if o.stock_code == symbol and o.order_type == order_type:
Expand All @@ -594,13 +674,20 @@ def is_order_exist(xtt: XtQuantTrader, acc: StockAccount, symbol, order_type, vo
return False


def is_allow_open(xtt: XtQuantTrader, acc: StockAccount, symbol, price, **kwargs):
def is_allow_open(xtt: XtQuantTrader, acc: StockAccount, symbol, **kwargs):
"""判断是否允许开仓
http://docs.thinktrader.net/pages/198696/#%E8%B5%84%E4%BA%A7xtasset
http://dict.thinktrader.net/nativeApi/xttrader.html?id=H018C2#%E8%B5%84%E4%BA%A7xtasset
:param xtt: XtQuantTrader, QMT 交易接口
:param acc: StockAccount, 账户
:param symbol: 股票代码
:param price: 股票现价
:param kwargs:
max_pos: int, 最大持仓数量
forbidden_symbols: list, 禁止交易的标的
multiple_orders: bool, 是否允许多次下单
:return: True 允许开仓,False 不允许开仓
"""
symbol_max_pos = kwargs.get("max_pos", 0) # 最大持仓数量
Expand All @@ -614,17 +701,16 @@ def is_allow_open(xtt: XtQuantTrader, acc: StockAccount, symbol, price, **kwargs
return False

# 如果 未成交的开仓委托单 存在,不允许开仓
if is_order_exist(xtt, acc, symbol, order_type=23):
logger.warning(f"存在未成交的开仓委托单,symbol={symbol}")
return False

# 如果已经有持仓,不允许开仓
if query_stock_positions(xtt, acc).get(symbol, None):
return False
multiple_orders = kwargs.get("multiple_orders", False)
if not multiple_orders:
if is_order_exist(xtt, acc, symbol, order_type=23):
logger.warning(f"存在未成交的开仓委托单,symbol={symbol}")
return False

# 如果资金不足,不允许开仓
assets = xtt.query_stock_asset(acc)
if assets.cash < price * 120:
symbol_price = xtdata.get_full_tick([symbol])[symbol]["lastPrice"]
if assets.cash < symbol_price * 120:
logger.warning(f"资金不足,无法开仓,symbol={symbol}")
return False

Expand All @@ -634,22 +720,33 @@ def is_allow_open(xtt: XtQuantTrader, acc: StockAccount, symbol, price, **kwargs
def is_allow_exit(xtt: XtQuantTrader, acc: StockAccount, symbol, **kwargs):
"""判断是否允许平仓
:param xtt: XtQuantTrader, QMT 交易接口
:param acc: StockAccount, 账户
:param symbol: 股票代码
:param kwargs:
forbidden_symbols: list, 禁止交易的标的
multiple_orders: bool, 是否允许多次下单
:return: True 允许开仓,False 不允许开仓
"""
# logger = kwargs.get("logger", logger)
# symbol 在禁止交易的列表中,不允许平仓
if symbol in kwargs.get("forbidden_symbols", []):
return False

# 没有持仓 或 可用数量为 0,不允许平仓
pos = query_stock_positions(xtt, acc).get(symbol, None)
if not pos or pos.can_use_volume <= 0:
logger.warning(f"没有持仓或可用数量为0,无法平仓,symbol={symbol}")
return False

# 未成交的平仓委托单 存在,不允许继续平仓
if is_order_exist(xtt, acc, symbol, order_type=24):
logger.warning(f"存在未成交的平仓委托单,symbol={symbol}")
return False
multiple_orders = kwargs.get("multiple_orders", False)
if not multiple_orders:
# 未成交的平仓委托单 存在,不允许继续平仓
if is_order_exist(xtt, acc, symbol, order_type=24):
logger.warning(f"存在未成交的平仓委托单,symbol={symbol}")
return False

return True

Expand Down Expand Up @@ -721,7 +818,7 @@ def order_stock_target(xtt: XtQuantTrader, acc: StockAccount, symbol, target, **
price = kwargs.get("price", 0)

# 如果目标小于当前,平仓
if target < current:
if target < current and is_allow_exit(xtt, acc, symbol, **kwargs):
delta = min(current - target, pos.can_use_volume if pos else current)
logger.info(f"{symbol}平仓,目标仓位:{target},当前仓位:{current},平仓数量:{delta}")
if delta != 0:
Expand All @@ -731,7 +828,7 @@ def order_stock_target(xtt: XtQuantTrader, acc: StockAccount, symbol, target, **
return

# 如果目标大于当前,开仓
if target > current:
if target > current and is_allow_open(xtt, acc, symbol, **kwargs):
delta = target - current
logger.info(f"{symbol}开仓,目标仓位:{target},当前仓位:{current},开仓数量:{delta}")
if delta != 0:
Expand Down
16 changes: 14 additions & 2 deletions czsc/connectors/tq_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,12 @@ def create_symbol_trader(api: TqApi, symbol, **kwargs):
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
# "[email protected]",
"[email protected]",
# https://www.jiaoyixingqiu.com/shouxufei/jiaoyisuo/CZCE
"[email protected]",
"[email protected]",
Expand All @@ -146,8 +148,11 @@ def create_symbol_trader(api: TqApi, symbol, **kwargs):
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
# https://www.jiaoyixingqiu.com/shouxufei/jiaoyisuo/DCE
"[email protected]",
Expand All @@ -170,25 +175,31 @@ def create_symbol_trader(api: TqApi, symbol, **kwargs):
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
# https://www.jiaoyixingqiu.com/shouxufei/jiaoyisuo/GFEX
"[email protected]",
"[email protected]",
# https://www.jiaoyixingqiu.com/shouxufei/jiaoyisuo/INE
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
# https://www.jiaoyixingqiu.com/shouxufei/jiaoyisuo/CFFEX
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
]


future_name_map = {
"AO": "氧化铝",
"PX": "对二甲苯",
"EC": "欧线集运",
"LC": "碳酸锂",
"PG": "LPG",
Expand Down Expand Up @@ -254,6 +265,7 @@ def create_symbol_trader(api: TqApi, symbol, **kwargs):
"AU": "黄金",
"PB": "沪铅",
"RU": "橡胶",
"BR": "合成橡胶",
"HC": "热轧卷板",
"BU": "沥青",
"SP": "纸浆",
Expand Down
25 changes: 17 additions & 8 deletions czsc/utils/st_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,6 @@ def show_event_return(df, factor, **kwargs):
:param kwargs: dict, 其他参数
- sub_title: str, 子标题
- max_overlap: int, 事件最大重叠次数
- max_unique: int, 因子独立值最大数量
"""
Expand All @@ -1059,21 +1058,28 @@ def show_event_return(df, factor, **kwargs):
c1, c2, c3, c4 = st.columns([1, 1, 1, 1])
agg_method = c1.selectbox(
"聚合方法",
["平均收益率", "收益中位数", "最小收益率", "盈亏比", "交易胜率"],
[
"平均收益率",
"收益中位数",
"盈亏比",
"交易胜率",
"前20%平均收益率",
"后20%平均收益率",
],
index=0,
key=f"agg_method_{factor}",
)
sdt = pd.to_datetime(c2.date_input("开始时间", value=df["dt"].min()))
edt = pd.to_datetime(c3.date_input("结束时间", value=df["dt"].max()))
max_overlap = c4.number_input("最大重叠次数", value=5, min_value=1, max_value=20)
max_overlap = c4.number_input("最大重叠次数", value=3, min_value=1, max_value=20)

df[factor] = df[factor].astype(str)
df = czsc.overlap(df, factor, new_col="overlap", max_overlap=max_overlap)
df = df[(df["dt"] >= sdt) & (df["dt"] <= edt)].copy()

st.write(
f"时间范围:{df['dt'].min().strftime('%Y%m%d')} ~ {df['dt'].max().strftime('%Y%m%d')};聚合方法:{agg_method}"
)
sdt = df["dt"].min().strftime("%Y-%m-%d")
edt = df["dt"].max().strftime("%Y-%m-%d")
st.write(f"时间范围:{sdt} ~ {edt};聚合方法:{agg_method}")
nb_cols = [x for x in df.columns.to_list() if x.startswith("n") and x.endswith("b")]

if agg_method == "平均收益率":
Expand All @@ -1082,8 +1088,11 @@ def show_event_return(df, factor, **kwargs):
if agg_method == "收益中位数":
agg_method = lambda x: np.median(x)

if agg_method == "最小收益率":
agg_method = lambda x: np.min(x)
if agg_method == "前20%平均收益率":
agg_method = lambda x: np.mean(sorted(x)[int(len(x) * 0.8) :])

if agg_method == "后20%平均收益率":
agg_method = lambda x: np.mean(sorted(x)[: int(len(x) * 0.2)])

if agg_method == "盈亏比":
agg_method = lambda x: np.mean([y for y in x if y > 0]) / abs(np.mean([y for y in x if y < 0]))
Expand Down

0 comments on commit 4eb79f7

Please sign in to comment.