MACD hist*2 对应到各种网站上展示的结果;重新优化POC计算逻辑,和coinank上对应
This commit is contained in:
99
chart.py
99
chart.py
@@ -41,7 +41,7 @@ def add_ema(df, chart, period: int = 50):
|
|||||||
|
|
||||||
def add_cci(df, chart, period: int = 14, height: float = 0.1, position: str = "bottom"):
|
def add_cci(df, chart, period: int = 14, height: float = 0.1, position: str = "bottom"):
|
||||||
cci = ta.CCI(df["high"], df["low"], df["close"], timeperiod=period)
|
cci = ta.CCI(df["high"], df["low"], df["close"], timeperiod=period)
|
||||||
df["cci"] = cci
|
df["CCI"] = cci
|
||||||
cci_chart = chart.create_subchart(
|
cci_chart = chart.create_subchart(
|
||||||
position=position, width=1, height=height, sync=True
|
position=position, width=1, height=height, sync=True
|
||||||
)
|
)
|
||||||
@@ -49,8 +49,8 @@ def add_cci(df, chart, period: int = 14, height: float = 0.1, position: str = "b
|
|||||||
visible=True, font_size=14, color="#FFFFFF", font_family="Times New Roman"
|
visible=True, font_size=14, color="#FFFFFF", font_family="Times New Roman"
|
||||||
)
|
)
|
||||||
cci_chart.time_scale(visible=False)
|
cci_chart.time_scale(visible=False)
|
||||||
cci_line = cci_chart.create_line(name="cci", color="#FF0000", width=2)
|
cci_line = cci_chart.create_line(name="CCI", color="#FF0000", width=2)
|
||||||
cci_line.set(df[["time", "cci"]])
|
cci_line.set(df[["time", "CCI"]])
|
||||||
df = df[["time"]].copy()
|
df = df[["time"]].copy()
|
||||||
df["h"] = 100
|
df["h"] = 100
|
||||||
df["l"] = -100
|
df["l"] = -100
|
||||||
@@ -74,13 +74,24 @@ def add_cci(df, chart, period: int = 14, height: float = 0.1, position: str = "b
|
|||||||
cci_line.set(df[["time", "l"]])
|
cci_line.set(df[["time", "l"]])
|
||||||
|
|
||||||
|
|
||||||
def add_macd(df, chart, height: float = 0.1, position: str = "bottom"):
|
def add_macd(
|
||||||
|
df,
|
||||||
|
chart,
|
||||||
|
fastperiod: int = 12,
|
||||||
|
slowperiod: int = 26,
|
||||||
|
signalperiod: int = 9,
|
||||||
|
height: float = 0.1,
|
||||||
|
position: str = "bottom",
|
||||||
|
):
|
||||||
macd, signal, hist = ta.MACD(
|
macd, signal, hist = ta.MACD(
|
||||||
df["close"], fastperiod=12, slowperiod=26, signalperiod=9
|
df["close"],
|
||||||
|
fastperiod=fastperiod,
|
||||||
|
slowperiod=slowperiod,
|
||||||
|
signalperiod=signalperiod,
|
||||||
)
|
)
|
||||||
df["macd"] = macd
|
df["DIF"] = macd
|
||||||
df["signal"] = signal
|
df["DEA"] = signal
|
||||||
df["histogram"] = hist
|
df["MACD"] = hist * 2
|
||||||
macd_chart = chart.create_subchart(
|
macd_chart = chart.create_subchart(
|
||||||
position=position, width=1, height=height, sync=True
|
position=position, width=1, height=height, sync=True
|
||||||
)
|
)
|
||||||
@@ -89,21 +100,21 @@ def add_macd(df, chart, height: float = 0.1, position: str = "bottom"):
|
|||||||
)
|
)
|
||||||
macd_chart.time_scale(visible=False)
|
macd_chart.time_scale(visible=False)
|
||||||
|
|
||||||
histogram = macd_chart.create_histogram(name="histogram")
|
histogram = macd_chart.create_histogram(name="MACD")
|
||||||
hist_data = df[["time", "histogram"]].copy()
|
hist_data = df[["time", "MACD"]].copy()
|
||||||
hist_data["color"] = hist_data["histogram"].apply(
|
hist_data["color"] = hist_data["MACD"].apply(
|
||||||
lambda x: "#00FF00" if x < 0 else "#ff0000" # 绿色 : 红色
|
lambda x: "#00FF00" if x < 0 else "#ff0000" # 绿色 : 红色
|
||||||
)
|
)
|
||||||
histogram.set(hist_data)
|
histogram.set(hist_data)
|
||||||
|
|
||||||
macd_line = macd_chart.create_line(
|
macd_line = macd_chart.create_line(
|
||||||
name="macd", color="#2962FF", width=2, price_label=False, price_line=False
|
name="DIF", color="#2962FF", width=2, price_label=False, price_line=False
|
||||||
)
|
)
|
||||||
macd_line.set(df[["time", "macd"]])
|
macd_line.set(df[["time", "DIF"]])
|
||||||
signal_line = macd_chart.create_line(
|
signal_line = macd_chart.create_line(
|
||||||
name="signal", color="#FF0000", width=2, price_label=False, price_line=False
|
name="DEA", color="#FF0000", width=2, price_label=False, price_line=False
|
||||||
)
|
)
|
||||||
signal_line.set(df[["time", "signal"]])
|
signal_line.set(df[["time", "DEA"]])
|
||||||
|
|
||||||
|
|
||||||
def TD(dataframe: pd.DataFrame):
|
def TD(dataframe: pd.DataFrame):
|
||||||
@@ -260,18 +271,49 @@ def resample_data(df: pd.DataFrame, timeframe: str) -> pd.DataFrame:
|
|||||||
|
|
||||||
|
|
||||||
def POC(df, bins: int = 50):
|
def POC(df, bins: int = 50):
|
||||||
|
"""
|
||||||
|
计算价格分布的成交量峰值位置(POC,Point Of Control)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
df: 包含 'low', 'high', 'volume' 三列的 DataFrame(每行代表一根 K 线)
|
||||||
|
bins: 将价格区间划分为多少个小区间(价格桶),默认为 50
|
||||||
|
|
||||||
|
返回:
|
||||||
|
poc: 代表成交量最大的价格区间的中点价格
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 当前数据的最低价和最高价,用于构建价格区间
|
||||||
low, high = df["low"].min(), df["high"].max()
|
low, high = df["low"].min(), df["high"].max()
|
||||||
edges = np.linspace(low, high, bins + 1)
|
# 将整个价格区间等分为 bins 个小区间,需要 bins+1 个边界点
|
||||||
# 为每根K线生成 bins 个均匀价格点,并分配等量成交量
|
price_ranges = np.linspace(low, high, bins + 1)
|
||||||
prices = np.concatenate(
|
|
||||||
[np.linspace(r["low"], r["high"], bins) for _, r in df.iterrows()]
|
# 初始化每个价格区间累积的成交量数组,长度为 bins(区间个数)
|
||||||
)
|
volume_per_price = np.zeros(bins)
|
||||||
volumes = np.repeat(df["volume"].values / bins, bins)
|
|
||||||
# 分箱求和
|
# 遍历每一根 K 线,将该 K 线的成交量按覆盖的价格区间平均分配
|
||||||
vol_profile, _ = np.histogram(prices, bins=edges, weights=volumes)
|
for i in range(len(df)):
|
||||||
# 返回最大成交量区间的中点
|
high = df["high"].iloc[i]
|
||||||
idx = np.argmax(vol_profile)
|
low = df["low"].iloc[i]
|
||||||
poc = (edges[idx] + edges[idx + 1]) / 2
|
volume = df["volume"].iloc[i]
|
||||||
|
|
||||||
|
# 找到当前 K 线覆盖的价格边界(注意使用闭区间判断)
|
||||||
|
# price_ranges 表示边界点,若 price_ranges[j] 在 [low, high] 范围内,则第 j 个边界被覆盖
|
||||||
|
price_coverage = (price_ranges >= low) & (price_ranges <= high)
|
||||||
|
covered_bins = np.where(price_coverage)[0]
|
||||||
|
|
||||||
|
if len(covered_bins) > 0:
|
||||||
|
# 如果覆盖了多个边界点,则这些边界之间形成了若干完整的区间
|
||||||
|
# 将该 K 线的成交量平均分配到这些被覆盖的区间上
|
||||||
|
# 注意:covered_bins 里是边界索引,最后一个边界索引对应的区间索引需小于 bins
|
||||||
|
volume_per_bin = volume / len(covered_bins)
|
||||||
|
for bin_idx in covered_bins:
|
||||||
|
if bin_idx < bins:
|
||||||
|
volume_per_price[bin_idx] += volume_per_bin
|
||||||
|
|
||||||
|
# 找到成交量最大的区间索引
|
||||||
|
idx = np.argmax(volume_per_price)
|
||||||
|
# 以该区间的两个边界的中点作为 POC 值
|
||||||
|
poc = (price_ranges[idx] + price_ranges[idx + 1]) / 2
|
||||||
return poc
|
return poc
|
||||||
|
|
||||||
|
|
||||||
@@ -281,6 +323,7 @@ def on_range_change_poc(chart, bars_before, bars_after):
|
|||||||
return
|
return
|
||||||
|
|
||||||
total_bars = len(df)
|
total_bars = len(df)
|
||||||
|
# TODO: k线拉到最早会报错
|
||||||
if bars_after < 0:
|
if bars_after < 0:
|
||||||
start_idx = max(0, int(bars_before))
|
start_idx = max(0, int(bars_before))
|
||||||
end_idx = total_bars
|
end_idx = total_bars
|
||||||
@@ -333,7 +376,7 @@ def plot_chart(df, symbol: str, name: str, timeframe: str):
|
|||||||
add_ema(df, chart, period=30)
|
add_ema(df, chart, period=30)
|
||||||
add_ema(df, chart, period=60)
|
add_ema(df, chart, period=60)
|
||||||
add_macd(df, chart)
|
add_macd(df, chart)
|
||||||
add_cci(df, chart, period=14)
|
add_cci(df, chart, period=26)
|
||||||
add_TD(df, chart)
|
add_TD(df, chart)
|
||||||
add_buy_sell_signal_markers(df, chart)
|
add_buy_sell_signal_markers(df, chart)
|
||||||
|
|
||||||
@@ -341,7 +384,7 @@ def plot_chart(df, symbol: str, name: str, timeframe: str):
|
|||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
symbol = "399986"
|
symbol = "399998"
|
||||||
timeframe = "1W"
|
timeframe = "1W"
|
||||||
|
|
||||||
df = pd.read_csv(
|
df = pd.read_csv(
|
||||||
|
|||||||
Reference in New Issue
Block a user