Skip to content

Commit

Permalink
V0.9.58 更新一批代码 (#211)
Browse files Browse the repository at this point in the history
* 0.9.58 start coding

* 0.9.58 read_table with empty

* 0.9.58 fix bug: start_qmt_exe

* 0.9.58 fix bug: remove_beta_effects

* 0.9.58 update

* 0.9.58 新增 judge_factor_direction

* 0.9.58 update

* 0.9.58 update

* 0.9.58 update

* 0.9.58 update

* 0.9.58 update

* 0.9.58 update

* 0.9.58 update

* 0.9.58 update

* 0.9.58 update

* 0.9.58 优化streamlit组件

* 0.9.58 fix bug

* 0.9.58 fix bug

* 0.9.58 fix bug
  • Loading branch information
zengbin93 authored Aug 31, 2024
1 parent 0046c33 commit daea50e
Show file tree
Hide file tree
Showing 18 changed files with 262 additions and 115 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/pythonpackage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Python package

on:
push:
branches: [ master, V0.9.57 ]
branches: [ master, V0.9.58 ]
pull_request:
branches: [ master ]

Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
>为什么要去了解其他理论,就是这些理论操作者的行为模式,将构成以后我们猎杀的对象,他们操作模式的缺陷,就是以后猎杀他们的最好武器,这就如同学独孤九剑,必须学会发现所有派别招数的缺陷,这也是本ID理论学习中一个极为关键的步骤。
>真正的预测,就是不测而测。所有预测的基础,就是分类,把所有可能的情况进行完全分类。有人可能说,分类以后,把不可能的排除,最后一个结果就是精确的。
>这是脑子锈了的想法,任何的排除,等价于一次预测,每排除一个分类,按概率的乘法原则,就使得最后的所谓精确变得越不精确,最后还是逃不掉概率的套子。
>对于预测分类的唯一正确原则就是不进行任何排除,而是要严格分清每种情况的边界条件。任何的分类,其实都等价于一个分段函数,就是要把这分段函数的边界条件确定清楚。
>边界条件分段后,就要确定一旦发生哪种情况就如何操作,也就是把操作也同样给分段化了。然后,把所有情况交给市场本身,让市场自己去当下选择。
>所有的操作,其实都是根据不同分段边界的一个结果,只是每个人的分段边界不同而已。因此,问题不是去预测什么,而是确定分段边界。
## 知识星球

Expand Down
6 changes: 4 additions & 2 deletions czsc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,15 @@
from czsc.eda import (
remove_beta_effects, vwap, twap,
cross_sectional_strategy,
judge_factor_direction,
monotonicity,
)


__version__ = "0.9.57"
__version__ = "0.9.58"
__author__ = "zengbin93"
__email__ = "[email protected]"
__date__ = "20240726"
__date__ = "20240808"


def welcome():
Expand Down
2 changes: 1 addition & 1 deletion czsc/connectors/qmt_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def start_qmt_exe(acc, pwd, qmt_exe, title, max_retry=6, **kwargs):

wait_seconds = kwargs.get("wait_seconds", 6)
i = 0
while not find_exe_window(acc):
while not find_exe_window(title):
if i > max_retry:
logger.warning(f"QMT连续{i}次尝试依旧无法启动,请人工检查!")
break
Expand Down
54 changes: 48 additions & 6 deletions czsc/eda.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,17 @@ def remove_beta_effects(df, **kwargs):
- factor: str, 因子列名
- betas: list, beta 列名列表
- linear_model: str, 线性模型,可选 ridge、linear 或 lasso
- linear_model_params: dict, 线性模型参数, 默认为空, 需要传入字典,根据模型不同参数不同
:return: DataFrame
"""

linear_model = kwargs.get("linear_model", "ridge")
linear_model_params = kwargs.get("linear_model_params", {})
linear = {
"ridge": Ridge(),
"linear": LinearRegression(),
"lasso": Lasso(),
"ridge": Ridge,
"linear": LinearRegression,
"lasso": Lasso,
}
assert linear_model in linear.keys(), "linear_model 参数必须为 ridge、linear 或 lasso"
Model = linear[linear_model]
Expand All @@ -71,7 +73,7 @@ def remove_beta_effects(df, **kwargs):

x = dfg[betas].values
y = dfg[factor].values
model = Model().fit(x, y)
model = Model(**linear_model_params).fit(x, y)
dfg[factor] = y - model.predict(x)
rows.append(dfg)

Expand Down Expand Up @@ -113,7 +115,47 @@ def cross_sectional_strategy(df, factor, **kwargs):

dfa = dfg.sort_values(factor, ascending=False).head(long_num)
dfb = dfg.sort_values(factor, ascending=True).head(short_num)
df.loc[dfa.index, "weight"] = 1 / long_num
df.loc[dfb.index, "weight"] = -1 / short_num
if long_num > 0:
df.loc[dfa.index, "weight"] = 1 / long_num
if short_num > 0:
df.loc[dfb.index, "weight"] = -1 / short_num

return df


def judge_factor_direction(df: pd.DataFrame, factor, target='n1b', by='symbol', **kwargs):
"""判断因子的方向,正向还是反向
:param df: pd.DataFrame, 数据源,必须包含 symbol, dt, target, factor 列
:param factor: str, 因子名称
:param target: str, 目标名称,默认为 n1b,表示下一根K线的涨跌幅
:param by: str, 分组字段,默认为 symbol,表示按品种分组(时序);也可以按 dt 分组,表示按时间分组(截面)
:param kwargs: dict, 其他参数
- method: str, 相关系数计算方法,默认为 pearson,可选 pearson, kendall, spearman
:return: str, positive or negative
"""
assert by in df.columns, f"数据中不存在 {by} 字段"
assert factor in df.columns, f"数据中不存在 {factor} 字段"
assert target in df.columns, f"数据中不存在 {target} 字段"

if by == "dt" and df['symbol'].nunique() < 2:
raise ValueError("品种数量过少,无法在时间截面上计算因子有效性方向")

if by == "symbol" and df['dt'].nunique() < 2:
raise ValueError("时间序列数据量过少,无法在品种上计算因子有效性方向")

method = kwargs.get("method", "pearson")
dfc = df.groupby(by)[[factor, target]].corr(method=method).unstack().iloc[:, 1].reset_index()
return "positive" if dfc[factor].mean().iloc[0] >= 0 else "negative"


def monotonicity(sequence):
"""计算序列的单调性
原理:计算序列与自然数序列的相关系数,系数越接近1,表示单调递增;系数越接近-1,表示单调递减;接近0表示无序
:param sequence: list, tuple 序列
:return: float, 单调性系数
"""
from scipy.stats import spearmanr
return spearmanr(sequence, range(len(sequence)))[0]
10 changes: 5 additions & 5 deletions czsc/fsa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
author: zengbin93
email: [email protected]
create_dt: 2022/12/16 19:37
describe:
describe:
"""

import requests
Expand Down Expand Up @@ -36,7 +36,7 @@ def push_text(text: str, key: str) -> None:
logger.error(f"推送消息失败: {e}")


def push_card(card: str, key: str) -> None:
def push_card(card: dict, key: str) -> None:
"""使用自定义机器人推送卡片消息到飞书群聊
如何在群组中使用机器人:
Expand Down Expand Up @@ -150,7 +150,7 @@ def update_spreadsheet(df: pd.DataFrame, spreadsheet_token: str, sheet_id: str,
获取 sheet_id - https://open.feishu.cn/document/server-docs/docs/sheets-v3/spreadsheet-sheet/query?appId=cli_a3077015cc39500e
:param df: datafream内容
:param df: dataframe内容
:param spreadsheet_token: 表格对应的token,url获取
:param sheet_id: 工作表的id
:param kwargs:
Expand All @@ -175,6 +175,6 @@ def update_spreadsheet(df: pd.DataFrame, spreadsheet_token: str, sheet_id: str,
else:
logger.error(b)
return 0
except Exception:
logger.exception("更新飞书表格失败")
except Exception as e:
logger.exception(f"更新飞书表格失败: {e}")
return 0
4 changes: 4 additions & 0 deletions czsc/fsa/bi_table.py
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,10 @@ def read_table(self, table_id, **kwargs):
rows = []
res = self.list_records(table_id, **kwargs)["data"]
total = res["total"]

if total == 0:
return pd.DataFrame()

rows.extend(res["items"])
while res["has_more"]:
res = self.list_records(table_id, page_token=res["page_token"], **kwargs)["data"]
Expand Down
8 changes: 7 additions & 1 deletion czsc/traders/weight_backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,13 @@ def backtest(self, n_jobs=1):
dfw = self.dfw.copy()
long_rate = dfw[dfw["weight"] > 0].shape[0] / dfw.shape[0]
short_rate = dfw[dfw["weight"] < 0].shape[0] / dfw.shape[0]
stats.update({"多头占比": long_rate, "空头占比": short_rate})
stats.update({"多头占比": round(long_rate, 4), "空头占比": round(short_rate, 4)})

alpha = self.alpha.copy()
stats["与基准相关性"] = round(alpha["策略"].corr(alpha["基准"]), 4)
alpha_short = alpha[alpha["基准"] < 0].copy()
stats["与基准空头相关性"] = round(alpha_short["策略"].corr(alpha_short["基准"]), 4)
stats["品种数量"] = len(symbols)

res["绩效评价"] = stats
return res
Expand Down
Loading

0 comments on commit daea50e

Please sign in to comment.