Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30219cfbdc | ||
|
|
93c19ce753 | ||
|
|
c81110214a |
@@ -71,9 +71,37 @@ class TelegramQuantStateMachine(QuantStateMachine):
|
|||||||
if self.current_state in ["STATE_1_OSCILLATION", "STATE_1_OSCILLATION_SQUEEZE"]:
|
if self.current_state in ["STATE_1_OSCILLATION", "STATE_1_OSCILLATION_SQUEEZE"]:
|
||||||
if self.current_state == "STATE_1_OSCILLATION_SQUEEZE":
|
if self.current_state == "STATE_1_OSCILLATION_SQUEEZE":
|
||||||
msg += "⚠️ *[变盘警告]*:弹簧已压紧,随时大突破,做T手速要快!\n"
|
msg += "⚠️ *[变盘警告]*:弹簧已压紧,随时大突破,做T手速要快!\n"
|
||||||
if f['percent_b'] >= 1.0 or f['min_bias'] > 0.035:
|
|
||||||
|
## 盘整阶段,三个指标会触发卖出提醒
|
||||||
|
## kdj死叉,且cci在下降中,且收盘价在5日均线下
|
||||||
|
## cci顶背离,且k线在d线之下,且收盘价在5日均线下
|
||||||
|
## cci从100以上下降到100以下,且k线在d线之下,且收盘价在5日均线下
|
||||||
|
|
||||||
|
if ((f['K'] < f['D'] and f['prev_K'] > f['prev_D']) ## kdj死叉
|
||||||
|
and (f['cci'] < f['prev_cci']) ## cci在下降中
|
||||||
|
and (f['close'] < f['ma5'])): ## 收盘价在5日均线下
|
||||||
msg += f"🟢 *【建议高抛】*:当前处于震荡高位(价格:{f['close']}),建议尾盘或明日开盘*手动卖出网格仓*!"
|
msg += f"🟢 *【建议高抛】*:当前处于震荡高位(价格:{f['close']}),建议尾盘或明日开盘*手动卖出网格仓*!"
|
||||||
elif f['percent_b'] <= 0.0 or f['min_bias'] < -0.035:
|
|
||||||
|
elif ((f['close'] > f['prev_close'] and f['cci'] < f['prev_cci']) ## cci顶背离
|
||||||
|
and (f['K'] < f['D']) ## k线在d线之下
|
||||||
|
and (f['close'] < f['ma5'])): ## 收盘价在5日均线下
|
||||||
|
msg += f"🟢 *【建议高抛】*:当前处于震荡高位(价格:{f['close']}),建议尾盘或明日开盘*手动卖出网格仓*!"
|
||||||
|
|
||||||
|
elif ((f['cci'] < 100 and f['prev_cci'] > 100) ## cci从100跌到100以下
|
||||||
|
and (f['K'] < f['D']) ## k线在d线之下
|
||||||
|
and (f['close'] < f['ma5'])): ## 收盘价在5日均线下
|
||||||
|
msg += f"🟢 *【建议高抛】*:当前处于震荡高位(价格:{f['close']}),建议尾盘或明日开盘*手动卖出网格仓*!"
|
||||||
|
|
||||||
|
## 盘整阶段,两个指标会触发买入提醒
|
||||||
|
## kdj金叉,且cci连续两天上涨,且收盘价在5日均线上
|
||||||
|
## cci连续两天上涨,且k线在d线上,且收盘价在5日均线上
|
||||||
|
elif ((f['cci'] > f['prev_cci'] and f['prev_cci'] > f['prev_cci_2']) ## cci连续两天上涨
|
||||||
|
and (f['K'] > f['D'] and f['prev_K'] <= f['prev_D']) ## kdj金叉
|
||||||
|
and (f['close'] > f['ma5'])): ## 收盘价站上五日线
|
||||||
|
msg += f"🔴 *【建议低吸】*:当前处于震荡超跌区(价格:{f['close']}),建议尾盘或明日开盘*手动买回筹码*!"
|
||||||
|
elif ((f['cci'] > f['prev_cci'] and f['prev_cci'] > f['prev_cci_2'] and f['prev_cci_2'] < f['prev_cci_3'])
|
||||||
|
and (f['K'] >= f['D'])
|
||||||
|
and (f['close'] > f['ma5'])):
|
||||||
msg += f"🔴 *【建议低吸】*:当前处于震荡超跌区(价格:{f['close']}),建议尾盘或明日开盘*手动买回筹码*!"
|
msg += f"🔴 *【建议低吸】*:当前处于震荡超跌区(价格:{f['close']}),建议尾盘或明日开盘*手动买回筹码*!"
|
||||||
else:
|
else:
|
||||||
msg += "⚪ *【建议观望】*:处于安全中枢内,未触及边界,明天*不要乱动*。"
|
msg += "⚪ *【建议观望】*:处于安全中枢内,未触及边界,明天*不要乱动*。"
|
||||||
|
|||||||
+51
-7
@@ -1,7 +1,7 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import talib
|
import talib
|
||||||
|
from util import calculate_brar, calc_kdj_tdx
|
||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# 1. 指标引擎:未来所有新发掘的指标,全写在这里
|
# 1. 指标引擎:未来所有新发掘的指标,全写在这里
|
||||||
@@ -57,8 +57,22 @@ class FeatureEngine:
|
|||||||
up, mid, low = talib.BBANDS(
|
up, mid, low = talib.BBANDS(
|
||||||
self.close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0
|
self.close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0
|
||||||
)
|
)
|
||||||
|
|
||||||
self.df["bb_up"], self.df["bb_mid"], self.df["bb_low"] = up, mid, low
|
self.df["bb_up"], self.df["bb_mid"], self.df["bb_low"] = up, mid, low
|
||||||
|
|
||||||
|
df_tmp = calc_kdj_tdx(self.df)
|
||||||
|
self.df["K"] = df_tmp['K']
|
||||||
|
self.df["D"] = df_tmp['D']
|
||||||
|
self.df["J"] = df_tmp['J']
|
||||||
|
|
||||||
|
cci = talib.CCI(self.df['high'], self.df['low'], self.df['close'], timeperiod=14)
|
||||||
|
self.df["cci"] = cci
|
||||||
|
|
||||||
|
df_tmp = calculate_brar(self.df)
|
||||||
|
|
||||||
|
self.df["ar"] = df_tmp["ar"]
|
||||||
|
self.df['br'] = df_tmp["br"]
|
||||||
|
|
||||||
self.df["percent_b"] = (self.df["close"] - low) / (up - low)
|
self.df["percent_b"] = (self.df["close"] - low) / (up - low)
|
||||||
self.df["bandwidth"] = (up - low) / mid
|
self.df["bandwidth"] = (up - low) / mid
|
||||||
self.df["amount_avg_20d"] = (
|
self.df["amount_avg_20d"] = (
|
||||||
@@ -69,6 +83,8 @@ class FeatureEngine:
|
|||||||
|
|
||||||
latest = self.df.iloc[-1]
|
latest = self.df.iloc[-1]
|
||||||
prev = self.df.iloc[-2]
|
prev = self.df.iloc[-2]
|
||||||
|
prev_2 = self.df.iloc[-3]
|
||||||
|
prev_3 = self.df.iloc[-4]
|
||||||
|
|
||||||
# 如果 5分钟线数据拿到了,现价以 5分钟最新收盘价为准(更接近 14:48 真实盘面)
|
# 如果 5分钟线数据拿到了,现价以 5分钟最新收盘价为准(更接近 14:48 真实盘面)
|
||||||
# 如果没拿到,退化使用日线昨日收盘(做测试用)
|
# 如果没拿到,退化使用日线昨日收盘(做测试用)
|
||||||
@@ -90,6 +106,26 @@ class FeatureEngine:
|
|||||||
)
|
)
|
||||||
features["is_not_new_low_10d"] = latest["close"] > latest["low_min_10d"]
|
features["is_not_new_low_10d"] = latest["close"] > latest["low_min_10d"]
|
||||||
|
|
||||||
|
features["prev_ma5"] = prev["ma5"] if "ma5" in prev else prev["close"]
|
||||||
|
|
||||||
|
features["K"] = latest["K"]
|
||||||
|
features["D"] = latest["D"]
|
||||||
|
features["J"] = latest["J"]
|
||||||
|
|
||||||
|
features["prev_K"] = prev["K"]
|
||||||
|
features["prev_D"] = prev["D"]
|
||||||
|
features["prev_J"] = prev["J"]
|
||||||
|
|
||||||
|
features["ar"] = latest["ar"]
|
||||||
|
features["prev_ar"] = prev["ar"]
|
||||||
|
features["br"] = latest["br"]
|
||||||
|
features["prev_br"] = prev["br"]
|
||||||
|
|
||||||
|
features["cci"] = latest["cci"]
|
||||||
|
features["prev_cci"] = prev["cci"]
|
||||||
|
features["prev_cci_2"] = prev_2["cci"]
|
||||||
|
features["prev_cci_3"] = prev_3["cci"]
|
||||||
|
|
||||||
history_bw = self.df["bandwidth"].iloc[-250:]
|
history_bw = self.df["bandwidth"].iloc[-250:]
|
||||||
features["bw_quantile"] = (history_bw < latest["bandwidth"]).mean()
|
features["bw_quantile"] = (history_bw < latest["bandwidth"]).mean()
|
||||||
|
|
||||||
@@ -113,13 +149,21 @@ class StateRuleBook:
|
|||||||
# 【判断是否从主升浪跌破】
|
# 【判断是否从主升浪跌破】
|
||||||
if (
|
if (
|
||||||
current_state == "STATE_3_MAIN_WAVE"
|
current_state == "STATE_3_MAIN_WAVE"
|
||||||
and f["close"] < f["ma10"]
|
and ((f["close"] < f["ma10"]
|
||||||
and f["prev_close"] > f["prev_ma10"]
|
and f["prev_close"] > f["prev_ma10"])
|
||||||
|
or ((f["prev_ar"] >= 150 and f["ar"] < 150)
|
||||||
|
and (f["prev_br"] >= 300 and f["br"] < 300)))
|
||||||
):
|
):
|
||||||
return (
|
if (f["close"] < f["ma10"] and f["prev_close"] > f["prev_ma10"]):
|
||||||
"STATE_4_WAVE_END",
|
return (
|
||||||
"⚠️ 主升浪确认结束!清空做T仓,准备重新激活做T。",
|
"STATE_4_WAVE_END",
|
||||||
)
|
"⚠️ 主升浪确认结束!清空做T仓,准备重新激活做T。(股价跌破10日均线)",
|
||||||
|
)
|
||||||
|
elif ((f["prev_ar"] >= 150 and f["ar"] < 150) and (f["prev_br"] >= 300 and f["br"] < 300)):
|
||||||
|
return (
|
||||||
|
"STATE_4_WAVE_END",
|
||||||
|
"⚠️ 主升浪确认结束!清空做T仓,准备重新激活做T。(ARBR跌破警戒线)",
|
||||||
|
)
|
||||||
|
|
||||||
# 【判断是否爆发主升浪】
|
# 【判断是否爆发主升浪】
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import requests
|
import requests
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
CN_CODE_TYPE = {
|
CN_CODE_TYPE = {
|
||||||
"600":'sh',
|
"600":'sh',
|
||||||
@@ -18,8 +19,77 @@ CN_CODE_TYPE = {
|
|||||||
"18":"sz"
|
"18":"sz"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
## arbr指标
|
||||||
|
def calculate_brar(df: pd.DataFrame, N: int = 26) -> pd.DataFrame:
|
||||||
|
"""
|
||||||
|
计算AR和BR情绪指标
|
||||||
|
|
||||||
|
参数:
|
||||||
|
df (pd.DataFrame): 包含 'high', 'low', 'open', 'close' 列的DataFrame
|
||||||
|
N (int): 计算周期,默认通常为26
|
||||||
|
返回:
|
||||||
|
pd.DataFrame: 包含原始数据及计算出的 'ar' 和 'br' 列的DataFrame
|
||||||
|
"""
|
||||||
|
# 创建副本,避免修改原始数据
|
||||||
|
data = df.copy()
|
||||||
|
|
||||||
|
# --- 计算 AR (人气指标) ---
|
||||||
|
# 公式: AR = (N日内 (最高价 - 开盘价) 之和) / (N日内 (开盘价 - 最低价) 之和) * 100
|
||||||
|
high_open = data['high'] - data['open']
|
||||||
|
open_low = data['open'] - data['low']
|
||||||
|
|
||||||
|
data['ar'] = (high_open.rolling(window=N, min_periods=1).sum() /
|
||||||
|
open_low.rolling(window=N, min_periods=1).sum()) * 100
|
||||||
|
|
||||||
|
# --- 计算 BR (意愿指标) ---
|
||||||
|
# 公式: BR = (N日内 MAX(0, 最高价 - 前一日收盘价) 之和) /
|
||||||
|
# (N日内 MAX(0, 前一日收盘价 - 最低价) 之和) * 100
|
||||||
|
prev_close = data['close'].shift(1)
|
||||||
|
|
||||||
|
# 计算多头意愿:当日最高价 - 前一日收盘价,若小于0则记为0
|
||||||
|
high_prev_close = data['high'] - prev_close
|
||||||
|
high_prev_close = high_prev_close.where(high_prev_close > 0, 0)
|
||||||
|
|
||||||
|
# 计算空头意愿:前一日收盘价 - 当日最低价,若小于0则记为0
|
||||||
|
prev_close_low = prev_close - data['low']
|
||||||
|
prev_close_low = prev_close_low.where(prev_close_low > 0, 0)
|
||||||
|
|
||||||
|
data['br'] = (high_prev_close.rolling(window=N, min_periods=1).sum() /
|
||||||
|
prev_close_low.rolling(window=N, min_periods=1).sum()) * 100
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
## kdj计算
|
||||||
|
def calc_kdj_tdx(df, n=9, m1=3, m2=3):
|
||||||
|
# 1. 计算 RSV
|
||||||
|
low_min = df['low'].rolling(n).min()
|
||||||
|
high_max = df['high'].rolling(n).max()
|
||||||
|
rsv = (df['close'] - low_min) / (high_max - low_min) * 100
|
||||||
|
rsv = rsv.fillna(0) # 或者处理 NaN
|
||||||
|
|
||||||
|
# 2. 自定义 SMA 函数(通达信风格)
|
||||||
|
def sma_tdx(series, period, weight):
|
||||||
|
# period=N, weight=M。公式:Y = (X*M + PREV_Y*(N-M))/N
|
||||||
|
result = np.zeros(len(series))
|
||||||
|
# 初始化第一个有效值。通常,如果数据足够,初始值为 series[0]
|
||||||
|
result[0] = series[0] if not np.isnan(series[0]) else 0
|
||||||
|
for i in range(1, len(series)):
|
||||||
|
# 如果 series[i] 是 NaN(例如,前 n 天),则保持 0 或向前填充,但通常 RSV 已填充。
|
||||||
|
val = series[i] if not np.isnan(series[i]) else 0
|
||||||
|
result[i] = (val * weight + result[i - 1] * (period - weight)) / period
|
||||||
|
return result
|
||||||
|
|
||||||
|
# 3. 计算 K 和 D
|
||||||
|
k_series = sma_tdx(rsv.values, m1, 1) # K = SMA(RSV, 3, 1)
|
||||||
|
d_series = sma_tdx(k_series, m2, 1) # D = SMA(K, 3, 1)
|
||||||
|
|
||||||
|
df['K'] = k_series
|
||||||
|
df['D'] = d_series
|
||||||
|
df['J'] = 3 * df['K'] - 2 * df['D']
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
|
## 获取历史k线
|
||||||
def get_history_k(code, start_date='19700101', end_date=None):
|
def get_history_k(code, start_date='19700101', end_date=None):
|
||||||
|
|
||||||
if '.' in code:
|
if '.' in code:
|
||||||
@@ -36,8 +106,7 @@ def get_history_k(code, start_date='19700101', end_date=None):
|
|||||||
url = "https://api.zhituapi.com/hs/history/%s/d/f?token=6E0E86BC-15AD-4275-8A95-B02D168D63C1&st=%s"%(stock_code, start_date)
|
url = "https://api.zhituapi.com/hs/history/%s/d/f?token=6E0E86BC-15AD-4275-8A95-B02D168D63C1&st=%s"%(stock_code, start_date)
|
||||||
|
|
||||||
if end_date is not None:
|
if end_date is not None:
|
||||||
url += "&end_date=%s"%(end_date)
|
url += "&et=%s"%(end_date)
|
||||||
|
|
||||||
response = requests.get(url)
|
response = requests.get(url)
|
||||||
|
|
||||||
data = response.json()
|
data = response.json()
|
||||||
@@ -55,6 +124,7 @@ def get_history_k(code, start_date='19700101', end_date=None):
|
|||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
## 获取当日5分钟的k线
|
||||||
def get_5min_k(code, start_date='19700101', end_date=None):
|
def get_5min_k(code, start_date='19700101', end_date=None):
|
||||||
stock_code = ''
|
stock_code = ''
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user