optimize trade strategy
This commit is contained in:
parent
1e204839a9
commit
43bd225cfa
|
|
@ -0,0 +1,29 @@
|
||||||
|
import schedule
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
import core.logger as logging
|
||||||
|
import subprocess
|
||||||
|
import os
|
||||||
|
|
||||||
|
logger = logging.logger
|
||||||
|
# 定义要执行的任务
|
||||||
|
def run_script():
|
||||||
|
start_time = time.time()
|
||||||
|
logger.info(f"Executing script at: {datetime.datetime.now()}")
|
||||||
|
output_file = r'./output/auto_schedule.txt'
|
||||||
|
with open(output_file, 'a') as f:
|
||||||
|
f.write(f"Task ran at {datetime.datetime.now()}\n")
|
||||||
|
python_path = r"D:\miniconda3\envs\okx\python.exe"
|
||||||
|
script_path = r"D:\python_projects\crypto_quant\huge_volume_main.py"
|
||||||
|
subprocess.run([python_path, script_path])
|
||||||
|
end_time = time.time()
|
||||||
|
logger.info(f"Script execution time: {end_time - start_time} seconds")
|
||||||
|
# 设置每小时运行一次
|
||||||
|
interval = 60 * 60
|
||||||
|
schedule.every(interval).seconds.do(run_script)
|
||||||
|
|
||||||
|
# 保持程序运行并检查调度
|
||||||
|
logger.info("Scheduler started. Press Ctrl+C to stop.")
|
||||||
|
while True:
|
||||||
|
schedule.run_pending()
|
||||||
|
time.sleep(1)
|
||||||
|
|
@ -171,9 +171,9 @@ class MaBreakStatistics:
|
||||||
ma_cross = str(ma_cross)
|
ma_cross = str(ma_cross)
|
||||||
buy_condition = False
|
buy_condition = False
|
||||||
if all_change:
|
if all_change:
|
||||||
buy_condition = (ma5 > ma10 and ma10 > ma20 and ma20 > ma30) and (close > ma20)
|
buy_condition = (ma_cross == "5上穿10") and (ma5 > ma10 and ma10 > ma20 and ma20 > ma30) and (close > ma20)
|
||||||
else:
|
else:
|
||||||
buy_condition = ma_cross == "5上穿10" and (ma5 > ma10)
|
buy_condition = (ma_cross == "5上穿10") and (ma5 > ma10)
|
||||||
if buy_condition:
|
if buy_condition:
|
||||||
ma_break_market_data_pair = {}
|
ma_break_market_data_pair = {}
|
||||||
ma_break_market_data_pair["symbol"] = symbol
|
ma_break_market_data_pair["symbol"] = symbol
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -4,7 +4,7 @@ import pandas as pd
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import seaborn as sns
|
import seaborn as sns
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
import re
|
import re
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
from openpyxl.drawing.image import Image
|
from openpyxl.drawing.image import Image
|
||||||
|
|
@ -45,21 +45,22 @@ class MeanReversionSandbox:
|
||||||
desc_dict = {
|
desc_dict = {
|
||||||
"买入": [
|
"买入": [
|
||||||
"1. 窗口周期为100, 即100个K线",
|
"1. 窗口周期为100, 即100个K线",
|
||||||
"2. 当前low_10_low为1, 即当前最低价格在窗口周期的10分位以下",
|
"2. 当前close_10_low为1, 即当前收盘价在窗口周期的10分位以下",
|
||||||
"3. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量",
|
"3. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量",
|
||||||
"4. 当前K线为阳线, 即close > open",
|
"4. 当前K线为阳线, 即close > open或者K线为一字, 长倒T线, 倒T线, 长十字星, 十字星",
|
||||||
|
# "5. 相同symbol的1H当前周期, ma5大于ma10",
|
||||||
|
# "5. KDJ, RSI, BOLL任意一个指标出现超卖",
|
||||||
],
|
],
|
||||||
"止损": ["跌幅超过下跌周期跌幅中位数, 即down_median后卖出"],
|
"止损": ["跌幅超过下跌周期跌幅中位数, 即down_median后卖出"],
|
||||||
"止盈": {
|
"止盈": {
|
||||||
"solution_1": [
|
"solution_1": [
|
||||||
"高位放量止盈 - 简易版",
|
"高位放量止盈 - 简易版",
|
||||||
"1. 当前high_80_high为1或者high_90_high为1",
|
"1. 当前close_80_high为1或者close_90_high为1",
|
||||||
"2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量",
|
"2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量",
|
||||||
],
|
],
|
||||||
"solution_2": [
|
"solution_2": [
|
||||||
"高位放量止盈 - 复杂版",
|
"高位放量止盈 - 复杂版",
|
||||||
"前提条件"
|
"前提条件" "1. 当前close_80_high为1或者close_90_high为1",
|
||||||
"1. 当前high_80_high为1或者high_90_high为1",
|
|
||||||
"2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量",
|
"2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量",
|
||||||
"以下两个条件, 任一满足即可",
|
"以下两个条件, 任一满足即可",
|
||||||
"1. K线为阴线, 即close < open",
|
"1. K线为阴线, 即close < open",
|
||||||
|
|
@ -68,9 +69,10 @@ class MeanReversionSandbox:
|
||||||
],
|
],
|
||||||
"solution_3": [
|
"solution_3": [
|
||||||
"上涨波段盈利中位数止盈法",
|
"上涨波段盈利中位数止盈法",
|
||||||
"1. 超过波段中位数涨幅, 即up_median后, 记录当前价格, 继续持仓",
|
"1. 超过波段中位数涨幅, 即up_median/ 到达价位90分位/ 任意技术指标出现中度超卖, 记录当前价格, 继续持仓",
|
||||||
"2. 之后一个周期, 如果价格上涨, 则记录该价格继续持仓",
|
"2. 之后一个周期, 如果价格上涨, 则记录该价格继续持仓",
|
||||||
"3. 之后一个周期, 如果价格跌到记录价格之下, 则卖出",
|
"3. 之后一个周期, 如果价格跌到记录价格之下, 则卖出",
|
||||||
|
"4. 如果买入时ma5小于ma10, 过程中ma5大于ma10, 进行记录。之后出现ma5小于ma10, 则卖出",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +162,8 @@ class MeanReversionSandbox:
|
||||||
trade_pair_dict["buy_huge_volume"] = row["huge_volume"]
|
trade_pair_dict["buy_huge_volume"] = row["huge_volume"]
|
||||||
trade_pair_dict["buy_volume_ratio"] = row["volume_ratio"]
|
trade_pair_dict["buy_volume_ratio"] = row["volume_ratio"]
|
||||||
trade_pair_dict["buy_k_shape"] = row["k_shape"]
|
trade_pair_dict["buy_k_shape"] = row["k_shape"]
|
||||||
trade_pair_dict["buy_low_10_low"] = row["low_10_low"]
|
trade_pair_dict["buy_close_10_low"] = row["close_10_low"]
|
||||||
|
trade_pair_dict["buy_ma5_lt_ma10"] = row["ma5"] < row["ma10"]
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if trade_pair_dict.get("buy_timestamp", None) is not None:
|
if trade_pair_dict.get("buy_timestamp", None) is not None:
|
||||||
|
|
@ -188,22 +191,27 @@ class MeanReversionSandbox:
|
||||||
trade_pair_dict["sell_huge_volume"] = row["huge_volume"]
|
trade_pair_dict["sell_huge_volume"] = row["huge_volume"]
|
||||||
trade_pair_dict["sell_volume_ratio"] = row["volume_ratio"]
|
trade_pair_dict["sell_volume_ratio"] = row["volume_ratio"]
|
||||||
trade_pair_dict["sell_k_shape"] = row["k_shape"]
|
trade_pair_dict["sell_k_shape"] = row["k_shape"]
|
||||||
trade_pair_dict["sell_high_80_high"] = row["high_80_high"]
|
trade_pair_dict["sell_close_80_high"] = row["close_80_high"]
|
||||||
trade_pair_dict["sell_high_90_high"] = row["high_90_high"]
|
trade_pair_dict["sell_close_90_high"] = row["close_90_high"]
|
||||||
trade_pair_dict["sell_low_10_low"] = row["low_10_low"]
|
trade_pair_dict["sell_close_10_low"] = row["close_10_low"]
|
||||||
trade_pair_dict["sell_low_20_low"] = row["low_20_low"]
|
trade_pair_dict["sell_close_20_low"] = row["close_20_low"]
|
||||||
trade_pair_dict["profit_pct"] = round(
|
trade_pair_dict["profit_pct"] = round(
|
||||||
(trade_pair_dict["sell_close"] - trade_pair_dict["buy_close"])
|
(trade_pair_dict["sell_close"] - trade_pair_dict["buy_close"])
|
||||||
/ trade_pair_dict["buy_close"]
|
/ trade_pair_dict["buy_close"]
|
||||||
* 100,
|
* 100,
|
||||||
4,
|
4,
|
||||||
)
|
)
|
||||||
if trade_pair_dict["sell_type"] == "止盈" and trade_pair_dict["profit_pct"] < 0:
|
if trade_pair_dict["profit_pct"] <= 0:
|
||||||
trade_pair_dict["sell_type"] = "止损"
|
trade_pair_dict["sell_type"] = "止损"
|
||||||
|
else:
|
||||||
|
trade_pair_dict["sell_type"] = "止盈"
|
||||||
if trade_pair_dict.get("last_max_close", None) is not None:
|
if trade_pair_dict.get("last_max_close", None) is not None:
|
||||||
# remove last_max_close
|
# remove last_max_close
|
||||||
trade_pair_dict.pop("last_max_close")
|
trade_pair_dict.pop("last_max_close")
|
||||||
|
|
||||||
|
if trade_pair_dict.get("process_ma5_gt_ma10", None) is not None:
|
||||||
|
trade_pair_dict.pop("process_ma5_gt_ma10")
|
||||||
|
|
||||||
trade_list.append(trade_pair_dict)
|
trade_list.append(trade_pair_dict)
|
||||||
trade_pair_dict = {}
|
trade_pair_dict = {}
|
||||||
|
|
||||||
|
|
@ -219,30 +227,75 @@ class MeanReversionSandbox:
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
买入条件
|
买入条件
|
||||||
1. 窗口周期为100, 即100个K线
|
1. 窗口周期为100, 即100个K线,
|
||||||
2. 当前low_10_low为1, 即当前最低价格在窗口周期的10分位以下
|
2. 当前close_10_low为1, 即当前收盘价在窗口周期的10分位以下,
|
||||||
3. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量
|
3. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量,
|
||||||
4. 当前K线为阳线, 即close > open
|
4. (当前K线为阳线, 即close > open)或者K线为一字, 长倒T线, 倒T线, 长十字星, 十字星,
|
||||||
5. TODO: 考虑K线形态
|
|
||||||
"""
|
"""
|
||||||
if index < 2:
|
if index < 2:
|
||||||
return False
|
return False
|
||||||
if row["close"] <= row["open"]:
|
if row["close"] <= row["open"] and row["k_shape"] not in [
|
||||||
|
"一字",
|
||||||
|
"长倒T线",
|
||||||
|
"倒T线",
|
||||||
|
"长十字星",
|
||||||
|
"十字星",
|
||||||
|
]:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if row["low_10_low"] != 1:
|
if row["close_10_low"] != 1:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# 如果当前与前两个K线,huge_volume都不为1,则返回False
|
# 如果当前与前两个K线, huge_volume都不为1, 则返回False
|
||||||
if (
|
if (
|
||||||
row["huge_volume"] != 1
|
row["huge_volume"] != 1
|
||||||
and market_data.loc[index - 1, "huge_volume"] != 1
|
and market_data.loc[index - 1, "huge_volume"] != 1
|
||||||
and market_data.loc[index - 2, "huge_volume"] != 1
|
and market_data.loc[index - 2, "huge_volume"] != 1
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
logger.info(f"符合买入条件")
|
|
||||||
|
# if not self.check_metrics_over_sell(row):
|
||||||
|
# return False
|
||||||
|
# latest_1h_data = self.get_latest_1h_data(row["symbol"], row["date_time"])
|
||||||
|
# if latest_1h_data is None or len(latest_1h_data) == 0:
|
||||||
|
# logger.info(f"符合买入条件")
|
||||||
|
# return True
|
||||||
|
# # 当前小时周期的ma5小于ma10, 表明空头趋势, 则返回False
|
||||||
|
# elif (
|
||||||
|
# not pd.isna(latest_1h_data["ma5"])
|
||||||
|
# and not pd.isna(latest_1h_data["ma10"])
|
||||||
|
# and latest_1h_data["ma5"] < latest_1h_data["ma10"]
|
||||||
|
# ):
|
||||||
|
# # logger.info(f"当前小时周期的ma5小于ma10, 空头趋势, 不符合买入条件")
|
||||||
|
# return False
|
||||||
|
# else:
|
||||||
|
# logger.info(f"符合买入条件")
|
||||||
|
# return True
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def get_latest_1h_data(self, symbol: str, current_date_time: str):
|
||||||
|
bar = "1H"
|
||||||
|
# 根据current_date_time, 获取当前时间往前推1H的日期时间,
|
||||||
|
# 如当前时间为2025-08-20 10:20:05, 则获取2025-08-20 09:00:00
|
||||||
|
before_date_time = datetime.strptime(current_date_time, "%Y-%m-%d %H:%M:%S")
|
||||||
|
before_date_time = before_date_time - timedelta(hours=1)
|
||||||
|
# current_date_time取整数小时,如2025-08-20 10:20:05, 取2025-08-20 10:00:00
|
||||||
|
before_date_time = before_date_time.replace(minute=0, second=0, microsecond=0)
|
||||||
|
before_date_time = before_date_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
end_date_time = datetime.strptime(current_date_time, "%Y-%m-%d %H:%M:%S")
|
||||||
|
end_date_time = end_date_time.replace(minute=0, second=0, microsecond=0)
|
||||||
|
end_date_time = end_date_time - timedelta(seconds=1)
|
||||||
|
end_date_time = end_date_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
latest_1h_data = self.db_merge_market_huge_volume.merge_market_huge_volume(
|
||||||
|
symbol, bar, 100, before_date_time, end_date_time
|
||||||
|
)
|
||||||
|
if latest_1h_data is None or len(latest_1h_data) == 0:
|
||||||
|
return None
|
||||||
|
# 只获取第一行数据
|
||||||
|
latest_1h_data = latest_1h_data.iloc[0]
|
||||||
|
return latest_1h_data
|
||||||
|
|
||||||
def check_stop_loss_condition(self, trade_pair_dict: dict, row: pd.Series):
|
def check_stop_loss_condition(self, trade_pair_dict: dict, row: pd.Series):
|
||||||
symbol = trade_pair_dict["symbol"]
|
symbol = trade_pair_dict["symbol"]
|
||||||
bar = trade_pair_dict["bar"]
|
bar = trade_pair_dict["bar"]
|
||||||
|
|
@ -282,9 +335,7 @@ class MeanReversionSandbox:
|
||||||
market_data, row, index
|
market_data, row, index
|
||||||
)
|
)
|
||||||
elif self.solution == "solution_3":
|
elif self.solution == "solution_3":
|
||||||
return self.check_take_profit_condition_solution_3(
|
return self.check_take_profit_condition_solution_3(trade_pair_dict, row)
|
||||||
trade_pair_dict, row
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Invalid strategy name: {self.solution}")
|
raise ValueError(f"Invalid strategy name: {self.solution}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -299,10 +350,10 @@ class MeanReversionSandbox:
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
高位放量止盈 - 简易版
|
高位放量止盈 - 简易版
|
||||||
1. 当前high_80_high为1或者high_90_high为1
|
1. 当前close_80_high为1或者close_90_high为1
|
||||||
2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量
|
2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量
|
||||||
"""
|
"""
|
||||||
if row["high_80_high"] != 1 and row["high_90_high"] != 1:
|
if row["close_80_high"] != 1 and row["close_90_high"] != 1:
|
||||||
return False
|
return False
|
||||||
if (
|
if (
|
||||||
row["huge_volume"] != 1
|
row["huge_volume"] != 1
|
||||||
|
|
@ -322,7 +373,7 @@ class MeanReversionSandbox:
|
||||||
"""
|
"""
|
||||||
高位放量止盈 - 复杂版
|
高位放量止盈 - 复杂版
|
||||||
前提条件
|
前提条件
|
||||||
1. 当前high_80_high为1或者high_90_high为1
|
1. 当前close_80_high为1或者close_90_high为1
|
||||||
2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量
|
2. 之前2个K线与当前K线, 存在任意一个K线huge_volume为1, 即存在一个K线是巨量
|
||||||
以下两个条件, 任一满足即可
|
以下两个条件, 任一满足即可
|
||||||
1. K线为阴线, 即close < open
|
1. K线为阴线, 即close < open
|
||||||
|
|
@ -334,25 +385,46 @@ class MeanReversionSandbox:
|
||||||
if row["close"] < row["open"]:
|
if row["close"] < row["open"]:
|
||||||
logger.info(f"符合高位放量止盈 - 复杂版条件")
|
logger.info(f"符合高位放量止盈 - 复杂版条件")
|
||||||
return True
|
return True
|
||||||
elif row["k_shape"] in ["一字", "长吊锤线", "吊锤线", "长倒T线", "倒T线", "长十字星", "十字星", "长上影线纺锤体", "长下影线纺锤体"]:
|
elif row["k_shape"] in [
|
||||||
|
"一字",
|
||||||
|
"长吊锤线",
|
||||||
|
"吊锤线",
|
||||||
|
"长倒T线",
|
||||||
|
"倒T线",
|
||||||
|
"长十字星",
|
||||||
|
"十字星",
|
||||||
|
"长上影线纺锤体",
|
||||||
|
"长下影线纺锤体",
|
||||||
|
]:
|
||||||
logger.info(f"符合高位放量止盈 - 复杂版条件")
|
logger.info(f"符合高位放量止盈 - 复杂版条件")
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def check_take_profit_condition_solution_3(
|
def check_take_profit_condition_solution_3(
|
||||||
self,
|
self, trade_pair_dict: dict, row: pd.Series
|
||||||
trade_pair_dict: dict,
|
|
||||||
row: pd.Series
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
上涨波段盈利中位数止盈法
|
上涨波段盈利阶段止盈法
|
||||||
1. 超过波段中位数涨幅, 即up_median后, 记录当前价格, 继续持仓
|
1. 超过波段中位数涨幅, 即up_median/ 到达价位90分位/ 任意技术指标出现中度超卖, 记录当前价格, 继续持仓
|
||||||
2. 之后一个周期, 如果价格上涨, 则记录该价格继续持仓
|
2. 之后一个周期, 如果价格上涨, 则记录该价格继续持仓
|
||||||
3. 之后一个周期, 如果价格跌到记录价格之下, 则卖出
|
3. 之后一个周期, 如果价格跌到记录价格之下, 则卖出
|
||||||
|
4. 如果买入时ma5小于ma10, 过程中ma5大于ma10, 进行记录。之后出现ma5小于ma10, 则卖出
|
||||||
"""
|
"""
|
||||||
current_close = row["close"]
|
current_close = row["close"]
|
||||||
last_max_close = trade_pair_dict.get("last_max_close", None)
|
last_max_close = trade_pair_dict.get("last_max_close", None)
|
||||||
|
|
||||||
|
if trade_pair_dict["buy_ma5_lt_ma10"]:
|
||||||
|
if trade_pair_dict.get("process_ma5_gt_ma10", None):
|
||||||
|
if row["ma5"] < row["ma10"]:
|
||||||
|
logger.info(f"MA5小于MA10发生转势, 卖出")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if row["ma5"] > row["ma10"]:
|
||||||
|
trade_pair_dict["process_ma5_gt_ma10"] = True
|
||||||
|
else:
|
||||||
|
trade_pair_dict["process_ma5_gt_ma10"] = False
|
||||||
|
|
||||||
if last_max_close is not None:
|
if last_max_close is not None:
|
||||||
if current_close >= last_max_close:
|
if current_close >= last_max_close:
|
||||||
logger.info(f"价格上涨, 继续持仓")
|
logger.info(f"价格上涨, 继续持仓")
|
||||||
|
|
@ -372,10 +444,73 @@ class MeanReversionSandbox:
|
||||||
].values[0]
|
].values[0]
|
||||||
/ 100
|
/ 100
|
||||||
)
|
)
|
||||||
|
|
||||||
|
need_record = False
|
||||||
buy_close = trade_pair_dict["buy_close"]
|
buy_close = trade_pair_dict["buy_close"]
|
||||||
price_chg = (current_close - buy_close) / buy_close
|
price_chg = (current_close - buy_close) / buy_close
|
||||||
if price_chg > up_median:
|
if price_chg > up_median:
|
||||||
logger.info(f"当前价格上涨超过波段中位数涨幅, 记录当前价格")
|
logger.info(f"当前价格上涨超过波段中位数涨幅, 记录当前价格")
|
||||||
|
need_record = True
|
||||||
|
elif self.check_metrics_over_buy(row):
|
||||||
|
logger.info(f"技术指标超买, 记录当前价格")
|
||||||
|
need_record = True
|
||||||
|
elif row["close_90_high"] == 1:
|
||||||
|
logger.info(f"到达价位90分位, 记录当前价格")
|
||||||
|
need_record = True
|
||||||
|
else:
|
||||||
|
need_record = False
|
||||||
|
if need_record:
|
||||||
trade_pair_dict["last_max_close"] = current_close
|
trade_pair_dict["last_max_close"] = current_close
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def check_metrics_over_buy(self, row: pd.Series):
|
||||||
|
"""
|
||||||
|
检查技术指标是否出现中度超买
|
||||||
|
KDJ
|
||||||
|
K:85.00
|
||||||
|
D:80.00
|
||||||
|
J:100.00
|
||||||
|
说明:K 和 D 进一步上升, J 显著高于100, 表示超买加剧, 回调概率增加, 但可能仍需确认。
|
||||||
|
RSI 14
|
||||||
|
RSI:80.00
|
||||||
|
说明:RSI 进一步上升, 超买程度加深, 市场可能接近短期顶部, 回调概率增加。
|
||||||
|
BOLL
|
||||||
|
价格位置:价格突破上轨, 偏离上轨约 +2.00%(即价格 = 上轨 × 1.02)
|
||||||
|
说明:价格显著突破上轨, 超买程度加深, 可能预示短期回调或反转
|
||||||
|
"""
|
||||||
|
if row["kdj_k"] > 85 and row["kdj_d"] > 80 and row["kdj_j"] > 100:
|
||||||
|
logger.info(f"KDJ超买")
|
||||||
|
return True
|
||||||
|
if row["rsi_14"] > 80:
|
||||||
|
logger.info(f"RSI超买")
|
||||||
|
return True
|
||||||
|
if row["boll_upper"] * 1.02 < row["close"]:
|
||||||
|
logger.info(f"BOLL超买")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def check_metrics_over_sell(self, row: pd.Series):
|
||||||
|
"""
|
||||||
|
检查技术指标是否出现超卖
|
||||||
|
KDJ
|
||||||
|
K: 25.00
|
||||||
|
D: 30.00
|
||||||
|
J: 20.00
|
||||||
|
|
||||||
|
RSI 14
|
||||||
|
RSI:30.00
|
||||||
|
说明: RSI 进一步下降, 超卖程度加深, 市场可能接近短期底部, 反弹概率增加。
|
||||||
|
|
||||||
|
BOLL
|
||||||
|
价格位置: 价格接近下轨
|
||||||
|
"""
|
||||||
|
if row["kdj_k"] < 25 and row["kdj_d"] < 30 and row["kdj_j"] < 20:
|
||||||
|
logger.info(f"KDJ超卖")
|
||||||
|
return True
|
||||||
|
if row["rsi_14"] < 30:
|
||||||
|
logger.info(f"RSI超卖")
|
||||||
|
return True
|
||||||
|
if row["boll_lower"] >= row["close"]:
|
||||||
|
logger.info(f"BOLL超卖")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
okx>=2.1.2
|
okx>=2.1.2
|
||||||
|
python-okx >= 0.3.9
|
||||||
pandas>=2.0.0
|
pandas>=2.0.0
|
||||||
requests>=2.25.0
|
requests>=2.25.0
|
||||||
sqlalchemy >= 2.0.41
|
sqlalchemy >= 2.0.41
|
||||||
pymysql >= 1.1.1
|
pymysql >= 1.1.1
|
||||||
wechatpy >= 1.8.18
|
wechatpy >= 1.8.18
|
||||||
seaborn >= 0.13.2
|
seaborn >= 0.13.2
|
||||||
schedule >= 1.2.2
|
schedule >= 1.2.2
|
||||||
|
xlsxwriter >= 3.2.5
|
||||||
|
openpyxl >= 3.1.5
|
||||||
|
cryptography >= 3.4.8
|
||||||
|
|
@ -23,14 +23,21 @@ logger = logging.logger
|
||||||
|
|
||||||
|
|
||||||
class MeanReversionSandboxMain:
|
class MeanReversionSandboxMain:
|
||||||
def __init__(self, start_date: str, end_date: str, window_size: int):
|
def __init__(self, start_date: str, end_date: str, window_size: int, only_5m: bool = False, solution_list: list = None):
|
||||||
self.symbols = MONITOR_CONFIG.get("volume_monitor", {}).get(
|
self.symbols = MONITOR_CONFIG.get("volume_monitor", {}).get(
|
||||||
"symbols", ["XCH-USDT"]
|
"symbols", ["XCH-USDT"]
|
||||||
)
|
)
|
||||||
self.bars = MONITOR_CONFIG.get("volume_monitor", {}).get(
|
self.only_5m = only_5m
|
||||||
"bars", ["5m", "15m", "30m", "1H"]
|
if only_5m:
|
||||||
)
|
self.bars = ["5m"]
|
||||||
self.solution_list = ["solution_1", "solution_2", "solution_3"]
|
else:
|
||||||
|
self.bars = MONITOR_CONFIG.get("volume_monitor", {}).get(
|
||||||
|
"bars", ["5m", "15m", "30m", "1H"]
|
||||||
|
)
|
||||||
|
if solution_list is None:
|
||||||
|
self.solution_list = ["solution_1", "solution_2", "solution_3"]
|
||||||
|
else:
|
||||||
|
self.solution_list = solution_list
|
||||||
self.start_date = start_date
|
self.start_date = start_date
|
||||||
self.end_date = end_date
|
self.end_date = end_date
|
||||||
self.window_size = window_size
|
self.window_size = window_size
|
||||||
|
|
@ -176,8 +183,13 @@ class MeanReversionSandboxMain:
|
||||||
sheet_name = f"{solution}_chart"
|
sheet_name = f"{solution}_chart"
|
||||||
chart_dict[sheet_name] = {}
|
chart_dict[sheet_name] = {}
|
||||||
for y_axis_field in y_axis_fields:
|
for y_axis_field in y_axis_fields:
|
||||||
# 绘制2x2的画布
|
if self.only_5m:
|
||||||
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
|
fig, axs = plt.subplots(1, 1, figsize=(10, 10))
|
||||||
|
# 当只有一个子图时,将axs包装成数组以便统一处理
|
||||||
|
axs = np.array([[axs]])
|
||||||
|
else:
|
||||||
|
# 绘制2x2的画布
|
||||||
|
fig, axs = plt.subplots(2, 2, figsize=(10, 10))
|
||||||
for j, bar in enumerate(bars_in_order):
|
for j, bar in enumerate(bars_in_order):
|
||||||
ax = axs[j // 2, j % 2]
|
ax = axs[j // 2, j % 2]
|
||||||
bar_data = stat_data[stat_data["bar"] == bar].copy()
|
bar_data = stat_data[stat_data["bar"] == bar].copy()
|
||||||
|
|
@ -191,6 +203,21 @@ class MeanReversionSandboxMain:
|
||||||
palette=colors,
|
palette=colors,
|
||||||
ax=ax,
|
ax=ax,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 在柱子上方添加数值标签
|
||||||
|
for i, (idx, row) in enumerate(bar_data.iterrows()):
|
||||||
|
value = row[y_axis_field]
|
||||||
|
# 根据数值类型格式化标签
|
||||||
|
if "ratio" in y_axis_field:
|
||||||
|
label = f"{value:.2f}%"
|
||||||
|
else:
|
||||||
|
label = f"{value:.4f}"
|
||||||
|
|
||||||
|
# 在柱子上方显示数值
|
||||||
|
ax.text(i, value, label,
|
||||||
|
ha='center', va='bottom',
|
||||||
|
fontsize=9, fontweight='bold')
|
||||||
|
|
||||||
ax.set_ylabel(y_axis_field)
|
ax.set_ylabel(y_axis_field)
|
||||||
ax.set_xlabel("symbol")
|
ax.set_xlabel("symbol")
|
||||||
ax.set_title(f"{solution} {bar}")
|
ax.set_title(f"{solution} {bar}")
|
||||||
|
|
@ -203,9 +230,10 @@ class MeanReversionSandboxMain:
|
||||||
label.set_horizontalalignment("right")
|
label.set_horizontalalignment("right")
|
||||||
# 隐藏未使用的subplot
|
# 隐藏未使用的subplot
|
||||||
total_used = len(bars_in_order)
|
total_used = len(bars_in_order)
|
||||||
for k in range(total_used, 4):
|
if not self.only_5m:
|
||||||
ax = axs[k // 2, k % 2]
|
for k in range(total_used, 4):
|
||||||
ax.axis("off")
|
ax = axs[k // 2, k % 2]
|
||||||
|
ax.axis("off")
|
||||||
fig.tight_layout()
|
fig.tight_layout()
|
||||||
file_name = f"{solution}_{y_axis_field}.png"
|
file_name = f"{solution}_{y_axis_field}.png"
|
||||||
fig.savefig(os.path.join(save_path, file_name))
|
fig.savefig(os.path.join(save_path, file_name))
|
||||||
|
|
@ -271,7 +299,8 @@ class MeanReversionSandboxMain:
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
start_date = "2025-05-15 00:00:00"
|
start_date = "2025-05-15 00:00:00"
|
||||||
end_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
end_date = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||||
|
solution_list = ["solution_3"]
|
||||||
mean_reversion_sandbox_main = MeanReversionSandboxMain(
|
mean_reversion_sandbox_main = MeanReversionSandboxMain(
|
||||||
start_date=start_date, end_date=end_date, window_size=100
|
start_date=start_date, end_date=end_date, window_size=100, only_5m=True, solution_list=solution_list
|
||||||
)
|
)
|
||||||
mean_reversion_sandbox_main.batch_mean_reversion_sandbox()
|
mean_reversion_sandbox_main.batch_mean_reversion_sandbox()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue