Skip to content

Commit

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

* 0.9.56 format codes

* 0.9.56 start coding

* 0.9.56 没有品种交易,直接return

* 0.9.56 logger 优化

* 0.9.56 新增ATR止盈止损信号

* 0.9.56 新增期货套利研究案例

* 0.9.56 update doc

* 0.9.56 新增策略上传接口的封装
  • Loading branch information
zengbin93 authored Jul 21, 2024
1 parent 277defc commit 7c26227
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 112 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.55 ]
branches: [ master, V0.9.56 ]
pull_request:
branches: [ master ]

Expand Down
4 changes: 2 additions & 2 deletions czsc/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,10 @@
)


__version__ = "0.9.55"
__version__ = "0.9.56"
__author__ = "zengbin93"
__email__ = "zeng_bin8888@163.com"
__date__ = "20240706"
__date__ = "20240714"


def welcome():
Expand Down
67 changes: 66 additions & 1 deletion czsc/connectors/cooperation.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
"""
import os
import czsc
import requests
import loguru
import pandas as pd
from tqdm import tqdm
from loguru import logger
from datetime import datetime
from czsc import RawBar, Freq

Expand Down Expand Up @@ -74,6 +75,8 @@ def get_symbols(name, **kwargs):

def get_min_future_klines(code, sdt, edt, freq="1m", **kwargs):
"""分段获取期货1分钟K线后合并"""
logger = kwargs.pop("logger", loguru.logger)

sdt = pd.to_datetime(sdt).strftime("%Y%m%d")
edt = pd.to_datetime(edt).strftime("%Y%m%d")
# dates = pd.date_range(start=sdt, end=edt, freq='1M')
Expand Down Expand Up @@ -133,6 +136,8 @@ def get_raw_bars(symbol, freq, sdt, edt, fq="前复权", **kwargs):
>>> from czsc.connectors import cooperation as coo
>>> df = coo.get_raw_bars(symbol="000001.SH#INDEX", freq="日线", sdt="2001-01-01", edt="2021-12-31", fq='后复权', raw_bars=False)
"""
logger = kwargs.pop("logger", loguru.logger)

freq = czsc.Freq(freq)
raw_bars = kwargs.get("raw_bars", True)
ttl = kwargs.get("ttl", -1)
Expand Down Expand Up @@ -226,3 +231,63 @@ def stocks_daily_klines(sdt="20170101", edt="20240101", **kwargs):
if nxb:
dfk = czsc.update_nxb(dfk, nseq=nxb)
return dfk


def upload_strategy(df, meta, token=None, **kwargs):
"""上传策略数据
:param df: pd.DataFrame, 策略持仓权重数据,至少包含 dt, symbol, weight 三列, 例如:
=================== ======== ========
dt symbol weight
=================== ======== ========
2017-01-03 09:01:00 ZZSF9001 0
2017-01-03 09:01:00 DLj9001 0
2017-01-03 09:01:00 SQag9001 0
2017-01-03 09:06:00 ZZSF9001 0.136364
2017-01-03 09:06:00 SQag9001 1
=================== ======== ========
:param meta: dict, 策略元数据
至少包含 name, description, base_freq, author, outsample_sdt 字段, 例如:
{'name': 'TS001_3',
'description': '测试策略:仅用于读写redis测试',
'base_freq': '1分钟',
'author': 'ZB',
'outsample_sdt': '20220101'}
:param token: str, 上传凭证码;如果不提供,将从环境变量 CZSC_TOKEN 中获取
:param kwargs: dict, 其他参数
- logger: loguru.logger, 日志记录器
:return dict
"""
logger = kwargs.pop("logger", loguru.logger)
df = df.copy()
df["dt"] = pd.to_datetime(df["dt"])
logger.info(f"输入数据中有 {len(df)} 条权重信号")

# 去除单个品种下相邻时间权重相同的数据
_res = []
for _, dfg in df.groupby("symbol"):
dfg = dfg.sort_values("dt", ascending=True).reset_index(drop=True)
dfg = dfg[dfg["weight"].diff().fillna(1) != 0].copy()
_res.append(dfg)
df = pd.concat(_res, ignore_index=True)
df = df.sort_values(["dt"]).reset_index(drop=True)
df["dt"] = df["dt"].dt.strftime("%Y-%m-%d %H:%M:%S")

logger.info(f"去除单个品种下相邻时间权重相同的数据后,剩余 {len(df)} 条权重信号")

data = {
"weights": df[["dt", "symbol", "weight"]].to_json(orient="split"),
"token": token or os.getenv("CZSC_TOKEN"),
"strategy_name": meta.get("name"),
"meta": meta,
}
response = requests.post("http://zbczsc.com:9106/upload_strategy", json=data)

logger.info(f"上传策略接口返回: {response.json()}")
return response.json()
10 changes: 10 additions & 0 deletions czsc/connectors/tq_connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,12 @@ def adjust_portfolio(api: TqApi, portfolio, account=None, **kwargs):
target_pos.set_target_volume(int(lots))
symbol_infos[symbol] = {"quote": quote, "target_pos": target_pos, "lots": lots}

if not symbol_infos:
logger.warning(f"没有需要调仓的品种,跳过调仓")
return api
else:
logger.info(f"开始调仓:{[x for x in symbol_infos.keys()]}")

while True:
api.wait_update()

Expand Down Expand Up @@ -439,6 +445,10 @@ def adjust_portfolio(api: TqApi, portfolio, account=None, **kwargs):

if (datetime.now() - start_time).seconds > timeout:
logger.error(f"调仓超时,已运行 {timeout} 秒")
for symbol, info in symbol_infos.items():
target_pos: TargetPosTask = info["target_pos"]
target_pos.cancel()
logger.info(f"取消调仓:{symbol}")
break

return api
1 change: 1 addition & 0 deletions czsc/signals/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@
pos_stop_V240331,
pos_stop_V240608,
pos_stop_V240614,
pos_stop_V240717,
)


Expand Down
74 changes: 69 additions & 5 deletions czsc/signals/pos.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ def pos_ma_V230414(cat: CzscTrader, **kwargs) -> OrderedDict:
**信号逻辑:**
多头止损逻辑如下,反之为空头止损逻辑
多头持有状态如下,反之为空头持有状态
1. 从多头开仓点开始,在给定对的K线周期 freq1 上向前找 N 个底分型,记为 F1
2. 将这 N 个底分型的最低点,记为 L1,如果 L1 的价格低于开仓点的价格,则止损
1. 如果持有多头,且开仓后有价格升破MA均线,则为多头升破均线;
2. 如果持有空头,且开仓后有价格跌破MA均线,则为空头跌破均线。
**信号列表:**
Expand Down Expand Up @@ -55,15 +55,13 @@ def pos_ma_V230414(cat: CzscTrader, **kwargs) -> OrderedDict:
c = cat.kas[freq1]
op = pos.operates[-1]

# 多头止损逻辑
if op["op"] == Operate.LO:
bars = [x for x in c.bars_raw[-100:] if x.dt > op["dt"]]
for x in bars:
if x.close > x.cache[key]:
v1, v2 = "多头", "升破均线"
break

# 空头止损逻辑
if op["op"] == Operate.SO:
bars = [x for x in c.bars_raw[-100:] if x.dt > op["dt"]]
for x in bars:
Expand Down Expand Up @@ -941,3 +939,69 @@ def pos_stop_V240614(cat: CzscTrader, **kwargs) -> OrderedDict:
v1 = "空头止损"

return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1)


def pos_stop_V240717(cat: CzscTrader, **kwargs) -> OrderedDict:
"""止损:多头开仓后,有超过N根K线的最低价在成本价-ATR*0.67下方,提示止损;空头反之。贡献者:谢磊
参数模板:"{pos_name}_{freq1}N{n}T{timeperiod}_止损V240717"
**信号逻辑:**
以多头止损为例,计算过程如下:
1. 从多头开仓点开始,在给定的K线周期 freq1 上获取开仓后的所有K线,记为 bars;
2. 计算 bars 中的最低价小于(开仓价-ATR*0.67)的数量,记为 C;
3. ATR的参数为默认参数,可以调整;
3. 如果 C >= N,则提示多头止损信号。
空头止损逻辑同理。
**信号列表:**
- Signal('SMA5多头_15分钟N3T20_止损V240614_多头止损_任意_任意_0')
- Signal('SMA5空头_15分钟N3T20_止损V240614_空头止损_任意_任意_0')
:param cat: CzscTrader对象
:param kwargs: 参数字典
- pos_name: str,开仓信号的名称
- freq1: str,给定的K线周期
- n: int,最低价下方N个价位,默认为 3
:return: OrderedDict
"""
from czsc.signals.tas import update_atr_cache

pos_name = kwargs["pos_name"]
freq1 = kwargs["freq1"]
n = int(kwargs.get("n", 10)) # N根K线
timeperiod = int(kwargs.get("timeperiod", 20)) # ATR参数

c = cat.kas[freq1]
cache_key = update_atr_cache(c, timeperiod=timeperiod)

k1, k2, k3 = f"{pos_name}_{freq1}N{n}T{timeperiod}_止损V240717".split("_")
v1 = "其他"

# 如果没有持仓策略,则不产生信号
if not cat.kas or not hasattr(cat, "positions"):
return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1)

pos = [x for x in cat.positions if x.name == pos_name][0]
if len(pos.operates) == 0 or pos.operates[-1]["op"] in [Operate.SE, Operate.LE]:
return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1)

op = pos.operates[-1]
atr = [x.cache[cache_key] if x.cache.get(cache_key) is not None else 0 for x in c.bars_raw if x.dt == op["dt"]]

# 开仓后的K线
a_bars = [x for x in c.bars_raw if x.dt >= op["dt"]]

if op["op"] == Operate.LO and len([x for x in a_bars if x.low < op["price"] - atr[0] * 0.67]) >= n:
v1 = "多头止损"

if op["op"] == Operate.SO and len([x for x in a_bars if x.high > op["price"] + atr[0] * 0.67]) >= n:
v1 = "空头止损"

return create_single_signal(k1=k1, k2=k2, k3=k3, v1=v1)
Loading

0 comments on commit 7c26227

Please sign in to comment.