parent
019b894c9a
commit
5b7e95f4d9
Binary file not shown.
Binary file not shown.
|
|
@ -7,10 +7,7 @@ logger = logging.logger
|
||||||
|
|
||||||
|
|
||||||
class DBBinanceData:
|
class DBBinanceData:
|
||||||
def __init__(
|
def __init__(self, db_url: str):
|
||||||
self,
|
|
||||||
db_url: str
|
|
||||||
):
|
|
||||||
self.db_url = db_url
|
self.db_url = db_url
|
||||||
self.table_name = "crypto_binance_data"
|
self.table_name = "crypto_binance_data"
|
||||||
self.columns = [
|
self.columns = [
|
||||||
|
|
@ -143,7 +140,9 @@ class DBBinanceData:
|
||||||
condition_dict = {"symbol": symbol, "bar": bar}
|
condition_dict = {"symbol": symbol, "bar": bar}
|
||||||
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
|
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
|
||||||
|
|
||||||
def query_data_before_timestamp(self, symbol: str, bar: str, timestamp: int, limit: int = 100):
|
def query_data_before_timestamp(
|
||||||
|
self, symbol: str, bar: str, timestamp: int, limit: int = 100
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
根据时间戳查询之前的数据
|
根据时间戳查询之前的数据
|
||||||
:param symbol: 交易对
|
:param symbol: 交易对
|
||||||
|
|
@ -157,7 +156,12 @@ class DBBinanceData:
|
||||||
ORDER BY timestamp DESC
|
ORDER BY timestamp DESC
|
||||||
LIMIT :limit
|
LIMIT :limit
|
||||||
"""
|
"""
|
||||||
condition_dict = {"symbol": symbol, "bar": bar, "timestamp": timestamp, "limit": limit}
|
condition_dict = {
|
||||||
|
"symbol": symbol,
|
||||||
|
"bar": bar,
|
||||||
|
"timestamp": timestamp,
|
||||||
|
"limit": limit,
|
||||||
|
}
|
||||||
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
|
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
|
||||||
|
|
||||||
def query_data_by_technical_indicators(
|
def query_data_by_technical_indicators(
|
||||||
|
|
@ -170,7 +174,7 @@ class DBBinanceData:
|
||||||
kdj_signal: str = None,
|
kdj_signal: str = None,
|
||||||
rsi_signal: str = None,
|
rsi_signal: str = None,
|
||||||
boll_signal: str = None,
|
boll_signal: str = None,
|
||||||
ma_cross: str = None
|
ma_cross: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
根据技术指标查询数据
|
根据技术指标查询数据
|
||||||
|
|
@ -230,7 +234,7 @@ class DBBinanceData:
|
||||||
bar: str,
|
bar: str,
|
||||||
signal: str = None,
|
signal: str = None,
|
||||||
start: str = None,
|
start: str = None,
|
||||||
end: str = None
|
end: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
查询MACD信号数据
|
查询MACD信号数据
|
||||||
|
|
@ -275,7 +279,7 @@ class DBBinanceData:
|
||||||
signal: str = None,
|
signal: str = None,
|
||||||
pattern: str = None,
|
pattern: str = None,
|
||||||
start: str = None,
|
start: str = None,
|
||||||
end: str = None
|
end: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
查询KDJ信号数据
|
查询KDJ信号数据
|
||||||
|
|
@ -325,7 +329,7 @@ class DBBinanceData:
|
||||||
long_short: str = None,
|
long_short: str = None,
|
||||||
divergence: str = None,
|
divergence: str = None,
|
||||||
start: str = None,
|
start: str = None,
|
||||||
end: str = None
|
end: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
查询均线信号数据
|
查询均线信号数据
|
||||||
|
|
@ -378,7 +382,7 @@ class DBBinanceData:
|
||||||
signal: str = None,
|
signal: str = None,
|
||||||
pattern: str = None,
|
pattern: str = None,
|
||||||
start: str = None,
|
start: str = None,
|
||||||
end: str = None
|
end: str = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
查询布林带信号数据
|
查询布林带信号数据
|
||||||
|
|
@ -421,11 +425,7 @@ class DBBinanceData:
|
||||||
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
|
return self.db_manager.query_data(sql, condition_dict, return_multi=True)
|
||||||
|
|
||||||
def get_technical_statistics(
|
def get_technical_statistics(
|
||||||
self,
|
self, symbol: str, bar: str, start: str = None, end: str = None
|
||||||
symbol: str,
|
|
||||||
bar: str,
|
|
||||||
start: str = None,
|
|
||||||
end: str = None
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
获取技术指标统计信息
|
获取技术指标统计信息
|
||||||
|
|
@ -473,17 +473,28 @@ class DBBinanceData:
|
||||||
|
|
||||||
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
|
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
|
||||||
|
|
||||||
def query_market_data_by_symbol_bar(self, symbol: str, bar: str, start: str = None, end: str = None):
|
def query_market_data_by_symbol_bar(
|
||||||
|
self,
|
||||||
|
symbol: str,
|
||||||
|
bar: str,
|
||||||
|
fields: list = None,
|
||||||
|
start: str = None,
|
||||||
|
end: str = None,
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
根据交易对和K线周期查询数据
|
根据交易对和K线周期查询数据
|
||||||
:param symbol: 交易对
|
:param symbol: 交易对
|
||||||
:param bar: K线周期
|
:param bar: K线周期
|
||||||
|
:param fields: 字段列表
|
||||||
:param start: 开始时间
|
:param start: 开始时间
|
||||||
:param end: 结束时间
|
:param end: 结束时间
|
||||||
"""
|
"""
|
||||||
|
if fields is None:
|
||||||
|
fields = ["*"]
|
||||||
|
fields_str = ", ".join(fields)
|
||||||
if start is None and end is None:
|
if start is None and end is None:
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_binance_data
|
SELECT {fields_str} FROM crypto_binance_data
|
||||||
WHERE symbol = :symbol AND bar = :bar
|
WHERE symbol = :symbol AND bar = :bar
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
|
|
@ -502,22 +513,27 @@ class DBBinanceData:
|
||||||
if start is not None and end is not None:
|
if start is not None and end is not None:
|
||||||
if start > end:
|
if start > end:
|
||||||
start, end = end, start
|
start, end = end, start
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_binance_data
|
SELECT {fields_str} FROM crypto_binance_data
|
||||||
WHERE symbol = :symbol AND bar = :bar AND timestamp BETWEEN :start AND :end
|
WHERE symbol = :symbol AND bar = :bar AND timestamp BETWEEN :start AND :end
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
condition_dict = {"symbol": symbol, "bar": bar, "start": start, "end": end}
|
condition_dict = {
|
||||||
|
"symbol": symbol,
|
||||||
|
"bar": bar,
|
||||||
|
"start": start,
|
||||||
|
"end": end,
|
||||||
|
}
|
||||||
elif start is not None:
|
elif start is not None:
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_binance_data
|
SELECT {fields_str} FROM crypto_binance_data
|
||||||
WHERE symbol = :symbol AND bar = :bar AND timestamp >= :start
|
WHERE symbol = :symbol AND bar = :bar AND timestamp >= :start
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
condition_dict = {"symbol": symbol, "bar": bar, "start": start}
|
condition_dict = {"symbol": symbol, "bar": bar, "start": start}
|
||||||
elif end is not None:
|
elif end is not None:
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_binance_data
|
SELECT {fields_str} FROM crypto_binance_data
|
||||||
WHERE symbol = :symbol AND bar = :bar AND timestamp <= :end
|
WHERE symbol = :symbol AND bar = :bar AND timestamp <= :end
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -218,13 +218,6 @@ class DBData:
|
||||||
:param db_url: 数据库连接URL
|
:param db_url: 数据库连接URL
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
engine = create_engine(
|
|
||||||
self.db_url,
|
|
||||||
pool_size=5, # 连接池大小
|
|
||||||
max_overflow=10, # 允许的最大溢出连接
|
|
||||||
pool_timeout=30, # 连接超时时间(秒)
|
|
||||||
pool_recycle=1800, # 连接回收时间(秒),避免长时间闲置
|
|
||||||
)
|
|
||||||
with self.db_engine.connect() as conn:
|
with self.db_engine.connect() as conn:
|
||||||
result = conn.execute(text(sql), condition_dict)
|
result = conn.execute(text(sql), condition_dict)
|
||||||
if return_multi:
|
if return_multi:
|
||||||
|
|
|
||||||
|
|
@ -473,17 +473,21 @@ class DBMarketData:
|
||||||
|
|
||||||
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
|
return self.db_manager.query_data(sql, condition_dict, return_multi=False)
|
||||||
|
|
||||||
def query_market_data_by_symbol_bar(self, symbol: str, bar: str, start: str = None, end: str = None):
|
def query_market_data_by_symbol_bar(self, symbol: str, bar: str, fields: list = None, start: str = None, end: str = None):
|
||||||
"""
|
"""
|
||||||
根据交易对和K线周期查询数据
|
根据交易对和K线周期查询数据
|
||||||
:param symbol: 交易对
|
:param symbol: 交易对
|
||||||
:param bar: K线周期
|
:param bar: K线周期
|
||||||
|
:param fields: 字段列表
|
||||||
:param start: 开始时间
|
:param start: 开始时间
|
||||||
:param end: 结束时间
|
:param end: 结束时间
|
||||||
"""
|
"""
|
||||||
|
if fields is None:
|
||||||
|
fields = ["*"]
|
||||||
|
fields_str = ", ".join(fields)
|
||||||
if start is None and end is None:
|
if start is None and end is None:
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_market_data
|
SELECT {fields_str} FROM crypto_market_data
|
||||||
WHERE symbol = :symbol AND bar = :bar
|
WHERE symbol = :symbol AND bar = :bar
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
|
|
@ -502,22 +506,22 @@ class DBMarketData:
|
||||||
if start is not None and end is not None:
|
if start is not None and end is not None:
|
||||||
if start > end:
|
if start > end:
|
||||||
start, end = end, start
|
start, end = end, start
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_market_data
|
SELECT {fields_str} FROM crypto_market_data
|
||||||
WHERE symbol = :symbol AND bar = :bar AND timestamp BETWEEN :start AND :end
|
WHERE symbol = :symbol AND bar = :bar AND timestamp BETWEEN :start AND :end
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
condition_dict = {"symbol": symbol, "bar": bar, "start": start, "end": end}
|
condition_dict = {"symbol": symbol, "bar": bar, "start": start, "end": end}
|
||||||
elif start is not None:
|
elif start is not None:
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_market_data
|
SELECT {fields_str} FROM crypto_market_data
|
||||||
WHERE symbol = :symbol AND bar = :bar AND timestamp >= :start
|
WHERE symbol = :symbol AND bar = :bar AND timestamp >= :start
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
condition_dict = {"symbol": symbol, "bar": bar, "start": start}
|
condition_dict = {"symbol": symbol, "bar": bar, "start": start}
|
||||||
elif end is not None:
|
elif end is not None:
|
||||||
sql = """
|
sql = f"""
|
||||||
SELECT * FROM crypto_market_data
|
SELECT {fields_str} FROM crypto_market_data
|
||||||
WHERE symbol = :symbol AND bar = :bar AND timestamp <= :end
|
WHERE symbol = :symbol AND bar = :bar AND timestamp <= :end
|
||||||
ORDER BY timestamp ASC
|
ORDER BY timestamp ASC
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -9,10 +9,12 @@ from openpyxl.drawing.image import Image
|
||||||
import openpyxl
|
import openpyxl
|
||||||
from openpyxl.styles import Font
|
from openpyxl.styles import Font
|
||||||
from PIL import Image as PILImage
|
from PIL import Image as PILImage
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
import core.logger as logging
|
import core.logger as logging
|
||||||
from config import OKX_MONITOR_CONFIG, MYSQL_CONFIG, WINDOW_SIZE
|
from config import OKX_MONITOR_CONFIG, MYSQL_CONFIG, WINDOW_SIZE
|
||||||
from core.db.db_market_data import DBMarketData
|
from core.db.db_market_data import DBMarketData
|
||||||
|
from core.db.db_binance_data import DBBinanceData
|
||||||
from core.db.db_huge_volume_data import DBHugeVolumeData
|
from core.db.db_huge_volume_data import DBHugeVolumeData
|
||||||
from core.utils import timestamp_to_datetime, transform_date_time_to_timestamp
|
from core.utils import timestamp_to_datetime, transform_date_time_to_timestamp
|
||||||
|
|
||||||
|
|
@ -35,11 +37,14 @@ class ORBStrategy:
|
||||||
commission_per_share=0.0005,
|
commission_per_share=0.0005,
|
||||||
profit_target_multiple=10,
|
profit_target_multiple=10,
|
||||||
is_us_stock=False,
|
is_us_stock=False,
|
||||||
|
is_binance=False,
|
||||||
direction=None,
|
direction=None,
|
||||||
by_sar=False,
|
by_sar=False,
|
||||||
symbol_bar_data=None,
|
symbol_bar_data=None,
|
||||||
|
symbol_1h_data=None,
|
||||||
price_range_mean_as_R=False,
|
price_range_mean_as_R=False,
|
||||||
by_big_k=False,
|
by_big_k=False,
|
||||||
|
by_1h_k=False,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
初始化ORB策略参数
|
初始化ORB策略参数
|
||||||
|
|
@ -63,10 +68,14 @@ class ORBStrategy:
|
||||||
:param commission_per_share: 每股交易佣金(美元,默认0.0005)
|
:param commission_per_share: 每股交易佣金(美元,默认0.0005)
|
||||||
:param profit_target_multiple: 盈利目标倍数(默认10倍$R,即10R)
|
:param profit_target_multiple: 盈利目标倍数(默认10倍$R,即10R)
|
||||||
:param is_us_stock: 是否是美股
|
:param is_us_stock: 是否是美股
|
||||||
|
:param is_binance: 是否是Binance
|
||||||
:param direction: 方向,None=自动,Long=多头,Short=空头
|
:param direction: 方向,None=自动,Long=多头,Short=空头
|
||||||
:param by_sar: 是否根据SAR指标生成信号,True=是,False=否
|
:param by_sar: 是否根据SAR指标生成信号,True=是,False=否
|
||||||
|
:param symbol_bar_data: 5分钟K线数据
|
||||||
|
:param symbol_1h_data: 1小时K线数据
|
||||||
:param price_range_mean_as_R: 是否将价格振幅均值作为$R,True=是,False=否
|
:param price_range_mean_as_R: 是否将价格振幅均值作为$R,True=是,False=否
|
||||||
:param by_big_k: 是否根据K线实体部分,亦即abs(open-close)超过high-low的50%,True=是,False=否
|
:param by_big_k: 是否根据K线实体部分,亦即abs(open-close)超过high-low的50%,True=是,False=否
|
||||||
|
:param by_1h_k: 是否根据1小时K线,True=是,False=否
|
||||||
"""
|
"""
|
||||||
logger.info(
|
logger.info(
|
||||||
f"初始化ORB策略参数:股票代码={symbol},K线周期={bar},开始日期={start_date},结束日期={end_date},初始账户资金={initial_capital},最大杠杆倍数={max_leverage},单次交易风险比例={risk_per_trade},每股交易佣金={commission_per_share}"
|
f"初始化ORB策略参数:股票代码={symbol},K线周期={bar},开始日期={start_date},结束日期={end_date},初始账户资金={initial_capital},最大杠杆倍数={max_leverage},单次交易风险比例={risk_per_trade},每股交易佣金={commission_per_share}"
|
||||||
|
|
@ -90,10 +99,15 @@ class ORBStrategy:
|
||||||
mysql_host = MYSQL_CONFIG.get("host", "localhost")
|
mysql_host = MYSQL_CONFIG.get("host", "localhost")
|
||||||
mysql_port = MYSQL_CONFIG.get("port", 3306)
|
mysql_port = MYSQL_CONFIG.get("port", 3306)
|
||||||
mysql_database = MYSQL_CONFIG.get("database", "okx")
|
mysql_database = MYSQL_CONFIG.get("database", "okx")
|
||||||
|
self.is_us_stock = is_us_stock
|
||||||
|
self.is_binance = is_binance
|
||||||
|
|
||||||
self.db_url = f"mysql+pymysql://{mysql_user}:{mysql_password}@{mysql_host}:{mysql_port}/{mysql_database}"
|
self.db_url = f"mysql+pymysql://{mysql_user}:{mysql_password}@{mysql_host}:{mysql_port}/{mysql_database}"
|
||||||
|
if self.is_binance:
|
||||||
|
self.db_market_data = DBBinanceData(self.db_url)
|
||||||
|
else:
|
||||||
self.db_market_data = DBMarketData(self.db_url)
|
self.db_market_data = DBMarketData(self.db_url)
|
||||||
self.is_us_stock = is_us_stock
|
|
||||||
self.output_chart_folder = r"./output/trade_sandbox/orb_strategy/chart/"
|
self.output_chart_folder = r"./output/trade_sandbox/orb_strategy/chart/"
|
||||||
self.output_excel_folder = r"./output/trade_sandbox/orb_strategy/excel/"
|
self.output_excel_folder = r"./output/trade_sandbox/orb_strategy/excel/"
|
||||||
os.makedirs(self.output_chart_folder, exist_ok=True)
|
os.makedirs(self.output_chart_folder, exist_ok=True)
|
||||||
|
|
@ -110,6 +124,7 @@ class ORBStrategy:
|
||||||
if self.by_sar:
|
if self.by_sar:
|
||||||
self.sar_desc = "考虑SAR"
|
self.sar_desc = "考虑SAR"
|
||||||
self.symbol_bar_data = symbol_bar_data
|
self.symbol_bar_data = symbol_bar_data
|
||||||
|
self.symbol_1h_data = symbol_1h_data
|
||||||
self.price_range_mean_as_R = price_range_mean_as_R
|
self.price_range_mean_as_R = price_range_mean_as_R
|
||||||
if self.price_range_mean_as_R:
|
if self.price_range_mean_as_R:
|
||||||
self.price_range_mean_as_R_desc = "R为振幅均值"
|
self.price_range_mean_as_R_desc = "R为振幅均值"
|
||||||
|
|
@ -121,6 +136,11 @@ class ORBStrategy:
|
||||||
self.by_big_k_desc = "K线实体过50%"
|
self.by_big_k_desc = "K线实体过50%"
|
||||||
else:
|
else:
|
||||||
self.by_big_k_desc = "无K线要求"
|
self.by_big_k_desc = "无K线要求"
|
||||||
|
self.by_1h_k = by_1h_k
|
||||||
|
if self.by_1h_k:
|
||||||
|
self.by_1h_k_desc = "参照1小时K线"
|
||||||
|
else:
|
||||||
|
self.by_1h_k_desc = "不参照1小时K线"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -132,7 +152,12 @@ class ORBStrategy:
|
||||||
if len(self.trades) > 0:
|
if len(self.trades) > 0:
|
||||||
self.plot_equity_curve()
|
self.plot_equity_curve()
|
||||||
self.output_trade_summary()
|
self.output_trade_summary()
|
||||||
return self.symbol_bar_data, self.trades_df, self.trades_summary_df
|
return (
|
||||||
|
self.symbol_bar_data,
|
||||||
|
self.symbol_1h_data,
|
||||||
|
self.trades_df,
|
||||||
|
self.trades_summary_df,
|
||||||
|
)
|
||||||
|
|
||||||
def fetch_intraday_data(self):
|
def fetch_intraday_data(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -146,47 +171,10 @@ class ORBStrategy:
|
||||||
f"开始获取{self.symbol}数据:{self.start_date}至{self.end_date},间隔{self.bar}"
|
f"开始获取{self.symbol}数据:{self.start_date}至{self.end_date},间隔{self.bar}"
|
||||||
)
|
)
|
||||||
if self.symbol_bar_data is None or len(self.symbol_bar_data) == 0:
|
if self.symbol_bar_data is None or len(self.symbol_bar_data) == 0:
|
||||||
data = self.db_market_data.query_market_data_by_symbol_bar(
|
self.data = self.get_full_data(bar=self.bar)
|
||||||
self.symbol, self.bar, start=self.start_date, end=self.end_date
|
|
||||||
)
|
|
||||||
data = pd.DataFrame(data)
|
|
||||||
data.sort_values(by="date_time", inplace=True)
|
|
||||||
# 保留核心列:开盘价、最高价、最低价、收盘价、成交量
|
|
||||||
data["Open"] = data["open"]
|
|
||||||
data["High"] = data["high"]
|
|
||||||
data["Low"] = data["low"]
|
|
||||||
data["Close"] = data["close"]
|
|
||||||
data["Volume"] = data["volume"]
|
|
||||||
if self.is_us_stock:
|
|
||||||
date_time_field = "date_time_us"
|
|
||||||
else:
|
|
||||||
date_time_field = "date_time"
|
|
||||||
data[date_time_field] = pd.to_datetime(data[date_time_field])
|
|
||||||
# data["Date"]为日期,不包括时分秒,即date_time如果是2025-01-01 10:00:00,则Date为2025-01-01
|
|
||||||
data["Date"] = data[date_time_field].dt.date
|
|
||||||
# 将Date转换为datetime64[ns]类型以确保类型一致
|
|
||||||
data["Date"] = pd.to_datetime(data["Date"])
|
|
||||||
# 最小data["Date"]
|
|
||||||
self.start_date = data["Date"].min().strftime("%Y-%m-%d")
|
|
||||||
# 最大data["Date"]
|
|
||||||
self.end_date = data["Date"].max().strftime("%Y-%m-%d")
|
|
||||||
self.data = data[
|
|
||||||
[
|
|
||||||
"symbol",
|
|
||||||
"bar",
|
|
||||||
"Date",
|
|
||||||
date_time_field,
|
|
||||||
"Open",
|
|
||||||
"High",
|
|
||||||
"Low",
|
|
||||||
"Close",
|
|
||||||
"Volume",
|
|
||||||
"sar_signal",
|
|
||||||
]
|
|
||||||
].copy()
|
|
||||||
self.data.rename(columns={date_time_field: "date_time"}, inplace=True)
|
|
||||||
self.calculate_price_range_mean()
|
self.calculate_price_range_mean()
|
||||||
self.symbol_bar_data = self.data.copy()
|
self.symbol_bar_data = self.data.copy()
|
||||||
|
self.symbol_1h_data = self.get_full_data(bar="1H")
|
||||||
else:
|
else:
|
||||||
self.data = self.symbol_bar_data.copy()
|
self.data = self.symbol_bar_data.copy()
|
||||||
|
|
||||||
|
|
@ -210,6 +198,91 @@ class ORBStrategy:
|
||||||
f"成功获取{self.symbol}数据:{len(self.data)}根{self.bar}K线,开始日期={self.start_date},结束日期={self.end_date}"
|
f"成功获取{self.symbol}数据:{len(self.data)}根{self.bar}K线,开始日期={self.start_date},结束日期={self.end_date}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_full_data(self, bar: str = "5m"):
|
||||||
|
"""
|
||||||
|
分段获取数据,并将数据合并为完整数据
|
||||||
|
分段依据:如果end_date与start_date相差超过一年,则每次取一年数据
|
||||||
|
"""
|
||||||
|
data = pd.DataFrame()
|
||||||
|
start_date = datetime.strptime(self.start_date, "%Y-%m-%d")
|
||||||
|
end_date = datetime.strptime(self.end_date, "%Y-%m-%d") + timedelta(days=1)
|
||||||
|
fields = [
|
||||||
|
"symbol",
|
||||||
|
"bar",
|
||||||
|
"date_time",
|
||||||
|
"date_time_us",
|
||||||
|
"open",
|
||||||
|
"high",
|
||||||
|
"low",
|
||||||
|
"close",
|
||||||
|
"volume",
|
||||||
|
"sar_signal",
|
||||||
|
"ma5",
|
||||||
|
"ma10",
|
||||||
|
"ma20",
|
||||||
|
"ma30",
|
||||||
|
"dif",
|
||||||
|
"macd",
|
||||||
|
]
|
||||||
|
while start_date < end_date:
|
||||||
|
current_end_date = min(start_date + timedelta(days=180), end_date)
|
||||||
|
start_date_str = start_date.strftime("%Y-%m-%d")
|
||||||
|
current_end_date_str = current_end_date.strftime("%Y-%m-%d")
|
||||||
|
logger.info(
|
||||||
|
f"获取{self.symbol}数据:{start_date_str}至{current_end_date_str}"
|
||||||
|
)
|
||||||
|
current_data = self.db_market_data.query_market_data_by_symbol_bar(
|
||||||
|
self.symbol, bar, fields, start=start_date_str, end=current_end_date_str
|
||||||
|
)
|
||||||
|
if current_data is not None and len(current_data) > 0:
|
||||||
|
current_data = pd.DataFrame(current_data)
|
||||||
|
data = pd.concat([data, current_data])
|
||||||
|
start_date = current_end_date
|
||||||
|
data.drop_duplicates(inplace=True)
|
||||||
|
if self.is_us_stock:
|
||||||
|
date_time_field = "date_time_us"
|
||||||
|
else:
|
||||||
|
date_time_field = "date_time"
|
||||||
|
data.sort_values(by=date_time_field, inplace=True)
|
||||||
|
data.reset_index(drop=True, inplace=True)
|
||||||
|
# 保留核心列:开盘价、最高价、最低价、收盘价、成交量
|
||||||
|
data["Open"] = data["open"]
|
||||||
|
data["High"] = data["high"]
|
||||||
|
data["Low"] = data["low"]
|
||||||
|
data["Close"] = data["close"]
|
||||||
|
data["Volume"] = data["volume"]
|
||||||
|
data[date_time_field] = pd.to_datetime(data[date_time_field])
|
||||||
|
# data["Date"]为日期,不包括时分秒,即date_time如果是2025-01-01 10:00:00,则Date为2025-01-01
|
||||||
|
data["Date"] = data[date_time_field].dt.date
|
||||||
|
# 将Date转换为datetime64[ns]类型以确保类型一致
|
||||||
|
data["Date"] = pd.to_datetime(data["Date"])
|
||||||
|
# 最小data["Date"]
|
||||||
|
self.start_date = data["Date"].min().strftime("%Y-%m-%d")
|
||||||
|
# 最大data["Date"]
|
||||||
|
self.end_date = data["Date"].max().strftime("%Y-%m-%d")
|
||||||
|
data = data[
|
||||||
|
[
|
||||||
|
"symbol",
|
||||||
|
"bar",
|
||||||
|
"Date",
|
||||||
|
date_time_field,
|
||||||
|
"Open",
|
||||||
|
"High",
|
||||||
|
"Low",
|
||||||
|
"Close",
|
||||||
|
"Volume",
|
||||||
|
"sar_signal",
|
||||||
|
"ma5",
|
||||||
|
"ma10",
|
||||||
|
"ma20",
|
||||||
|
"ma30",
|
||||||
|
"dif",
|
||||||
|
"macd",
|
||||||
|
]
|
||||||
|
]
|
||||||
|
data.rename(columns={date_time_field: "date_time"}, inplace=True)
|
||||||
|
return data
|
||||||
|
|
||||||
def calculate_shares(self, account_value, entry_price, stop_price, risk_assumed):
|
def calculate_shares(self, account_value, entry_price, stop_price, risk_assumed):
|
||||||
"""
|
"""
|
||||||
根据ORB公式计算交易股数
|
根据ORB公式计算交易股数
|
||||||
|
|
@ -247,7 +320,7 @@ class ORBStrategy:
|
||||||
- 第二根5分钟K线:根据第一根K线方向生成多空信号
|
- 第二根5分钟K线:根据第一根K线方向生成多空信号
|
||||||
"""
|
"""
|
||||||
logger.info(
|
logger.info(
|
||||||
f"开始生成ORB策略信号:{self.direction_desc},根据SAR指标:{self.by_sar}"
|
f"开始生成ORB策略信号:{self.direction_desc},根据SAR指标:{self.by_sar},{self.by_1h_k_desc}"
|
||||||
)
|
)
|
||||||
if self.data is None:
|
if self.data is None:
|
||||||
raise ValueError("请先调用fetch_intraday_data获取数据")
|
raise ValueError("请先调用fetch_intraday_data获取数据")
|
||||||
|
|
@ -261,6 +334,7 @@ class ORBStrategy:
|
||||||
|
|
||||||
# 第一根5分钟K线(开盘区间)
|
# 第一根5分钟K线(开盘区间)
|
||||||
first_candle = daily_data.iloc[0]
|
first_candle = daily_data.iloc[0]
|
||||||
|
current_date = first_candle["Date"]
|
||||||
high1 = first_candle["High"]
|
high1 = first_candle["High"]
|
||||||
low1 = first_candle["Low"]
|
low1 = first_candle["Low"]
|
||||||
open1 = first_candle["Open"]
|
open1 = first_candle["Open"]
|
||||||
|
|
@ -273,6 +347,25 @@ class ORBStrategy:
|
||||||
if (abs(open1 - close1) / (high1 - low1)) < 0.5:
|
if (abs(open1 - close1) / (high1 - low1)) < 0.5:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
ma5_1h = None
|
||||||
|
ma10_1h = None
|
||||||
|
dif_1h = None
|
||||||
|
macd_1h = None
|
||||||
|
if self.by_1h_k:
|
||||||
|
if self.symbol_1h_data is None or len(self.symbol_1h_data) == 0:
|
||||||
|
continue
|
||||||
|
if len(self.symbol_1h_data) < 2:
|
||||||
|
continue
|
||||||
|
symbol_1h_date_data = self.symbol_1h_data[
|
||||||
|
self.symbol_1h_data["Date"] == current_date
|
||||||
|
]
|
||||||
|
if len(symbol_1h_date_data) > 0:
|
||||||
|
first_candle_1h = symbol_1h_date_data.iloc[0]
|
||||||
|
ma5_1h = first_candle_1h["ma5"]
|
||||||
|
ma10_1h = first_candle_1h["ma10"]
|
||||||
|
dif_1h = first_candle_1h["dif"]
|
||||||
|
macd_1h = first_candle_1h["macd"]
|
||||||
|
|
||||||
# 第二根5分钟K线(entry信号)
|
# 第二根5分钟K线(entry信号)
|
||||||
second_candle = daily_data.iloc[1]
|
second_candle = daily_data.iloc[1]
|
||||||
entry_price = second_candle["Open"] # entry价格=第二根K线开盘价
|
entry_price = second_candle["Open"] # entry价格=第二根K线开盘价
|
||||||
|
|
@ -283,6 +376,22 @@ class ORBStrategy:
|
||||||
open1 < close1
|
open1 < close1
|
||||||
and (self.direction == "Long" or self.direction is None)
|
and (self.direction == "Long" or self.direction is None)
|
||||||
and ((self.by_sar and sar_signal == "SAR多头") or not self.by_sar)
|
and ((self.by_sar and sar_signal == "SAR多头") or not self.by_sar)
|
||||||
|
and (
|
||||||
|
(
|
||||||
|
self.by_1h_k
|
||||||
|
and (
|
||||||
|
ma5_1h is not None
|
||||||
|
and ma10_1h is not None
|
||||||
|
and ma5_1h > ma10_1h
|
||||||
|
)
|
||||||
|
and (
|
||||||
|
dif_1h is not None
|
||||||
|
and macd_1h is not None
|
||||||
|
and (dif_1h > 0 or macd_1h > 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or not self.by_1h_k
|
||||||
|
)
|
||||||
):
|
):
|
||||||
# 第一根K线收涨→多头信号
|
# 第一根K线收涨→多头信号
|
||||||
signal = "Long"
|
signal = "Long"
|
||||||
|
|
@ -291,6 +400,22 @@ class ORBStrategy:
|
||||||
open1 > close1
|
open1 > close1
|
||||||
and (self.direction == "Short" or self.direction is None)
|
and (self.direction == "Short" or self.direction is None)
|
||||||
and ((self.by_sar and sar_signal == "SAR空头") or not self.by_sar)
|
and ((self.by_sar and sar_signal == "SAR空头") or not self.by_sar)
|
||||||
|
and (
|
||||||
|
(
|
||||||
|
self.by_1h_k
|
||||||
|
and (
|
||||||
|
ma5_1h is not None
|
||||||
|
and ma10_1h is not None
|
||||||
|
and ma5_1h < ma10_1h
|
||||||
|
)
|
||||||
|
and (
|
||||||
|
dif_1h is not None
|
||||||
|
and macd_1h is not None
|
||||||
|
and (dif_1h < 0 or macd_1h < 0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
or not self.by_1h_k
|
||||||
|
)
|
||||||
):
|
):
|
||||||
# 第一根K线收跌→空头信号
|
# 第一根K线收跌→空头信号
|
||||||
signal = "Short"
|
signal = "Short"
|
||||||
|
|
@ -477,6 +602,7 @@ class ORBStrategy:
|
||||||
"BySar": self.sar_desc,
|
"BySar": self.sar_desc,
|
||||||
"PriceRangeMeanAsR": self.price_range_mean_as_R_desc,
|
"PriceRangeMeanAsR": self.price_range_mean_as_R_desc,
|
||||||
"ByBigK": self.by_big_k_desc,
|
"ByBigK": self.by_big_k_desc,
|
||||||
|
"By1hK": self.by_1h_k_desc,
|
||||||
"Symbol": self.symbol,
|
"Symbol": self.symbol,
|
||||||
"Bar": self.bar,
|
"Bar": self.bar,
|
||||||
"Date": date,
|
"Date": date,
|
||||||
|
|
@ -576,6 +702,7 @@ class ORBStrategy:
|
||||||
self.trades_summary["根据SAR"] = self.sar_desc
|
self.trades_summary["根据SAR"] = self.sar_desc
|
||||||
self.trades_summary["R算法"] = self.price_range_mean_as_R_desc
|
self.trades_summary["R算法"] = self.price_range_mean_as_R_desc
|
||||||
self.trades_summary["K线条件"] = self.by_big_k_desc
|
self.trades_summary["K线条件"] = self.by_big_k_desc
|
||||||
|
self.trades_summary["1小时K线条件"] = self.by_1h_k_desc
|
||||||
self.trades_summary["股票代码"] = self.symbol
|
self.trades_summary["股票代码"] = self.symbol
|
||||||
self.trades_summary["K线周期"] = self.bar
|
self.trades_summary["K线周期"] = self.bar
|
||||||
self.trades_summary["开始日期"] = self.start_date
|
self.trades_summary["开始日期"] = self.start_date
|
||||||
|
|
@ -660,7 +787,7 @@ class ORBStrategy:
|
||||||
markersize=4,
|
markersize=4,
|
||||||
)
|
)
|
||||||
plt.title(
|
plt.title(
|
||||||
f"{symbol} {bar} {self.direction_desc} {self.sar_desc} {self.price_range_mean_as_R_desc} {self.by_big_k_desc}",
|
f"{symbol} {bar} {self.direction_desc} {self.sar_desc} {self.price_range_mean_as_R_desc} {self.by_big_k_desc} {self.by_1h_k_desc}",
|
||||||
fontsize=14,
|
fontsize=14,
|
||||||
fontweight="bold",
|
fontweight="bold",
|
||||||
)
|
)
|
||||||
|
|
@ -704,7 +831,7 @@ class ORBStrategy:
|
||||||
fontsize=10,
|
fontsize=10,
|
||||||
)
|
)
|
||||||
plt.tight_layout()
|
plt.tight_layout()
|
||||||
self.chart_save_path = f"{self.output_chart_folder}/{symbol}_{bar}_{self.direction_desc}_{self.sar_desc}_{self.price_range_mean_as_R_desc}_{self.by_big_k_desc}_orb.png"
|
self.chart_save_path = f"{self.output_chart_folder}/{symbol}_{bar}_{self.direction_desc}_{self.sar_desc}_{self.price_range_mean_as_R_desc}_{self.by_big_k_desc}_{self.by_1h_k_desc}_orb.png"
|
||||||
plt.savefig(self.chart_save_path, dpi=150, bbox_inches="tight")
|
plt.savefig(self.chart_save_path, dpi=150, bbox_inches="tight")
|
||||||
plt.close()
|
plt.close()
|
||||||
|
|
||||||
|
|
@ -714,7 +841,7 @@ class ORBStrategy:
|
||||||
"""
|
"""
|
||||||
start_date = self.start_date.replace("-", "")
|
start_date = self.start_date.replace("-", "")
|
||||||
end_date = self.end_date.replace("-", "")
|
end_date = self.end_date.replace("-", "")
|
||||||
output_file_name = f"orb_{self.symbol}_{self.bar}_{start_date}_{end_date}_{self.direction_desc}_{self.sar_desc}_{self.price_range_mean_as_R_desc}_{self.by_big_k_desc}.xlsx"
|
output_file_name = f"orb_{self.symbol}_{self.bar}_{start_date}_{end_date}_{self.direction_desc}_{self.sar_desc}_{self.price_range_mean_as_R_desc}_{self.by_big_k_desc}_{self.by_1h_k_desc}.xlsx"
|
||||||
output_file_path = os.path.join(self.output_excel_folder, output_file_name)
|
output_file_path = os.path.join(self.output_excel_folder, output_file_name)
|
||||||
logger.info(f"导出{output_file_path}")
|
logger.info(f"导出{output_file_path}")
|
||||||
with pd.ExcelWriter(output_file_path) as writer:
|
with pd.ExcelWriter(output_file_path) as writer:
|
||||||
|
|
|
||||||
|
|
@ -678,9 +678,9 @@ def batch_import_binance_data_by_csv():
|
||||||
|
|
||||||
def test_import_binance_data_by_csv():
|
def test_import_binance_data_by_csv():
|
||||||
huge_volume_main = HugeVolumeMain(threshold=2.0, is_us_stock=False, is_binance=True)
|
huge_volume_main = HugeVolumeMain(threshold=2.0, is_us_stock=False, is_binance=True)
|
||||||
file_path = "./data/binance/spot/2020-08-11/SOL-USDT_1h.csv"
|
file_path = "./data/binance/spot/2020-08-12/XRP-USDT_1h.csv"
|
||||||
huge_volume_main.import_binance_data_by_csv(
|
huge_volume_main.import_binance_data_by_csv(
|
||||||
file_path, "SOL-USDT", "1H", [50, 80, 100, 120]
|
file_path, "XRP-USDT", "1H", [50, 80, 100, 120]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -696,8 +696,8 @@ def test_send_huge_volume_data_to_wechat():
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_import_binance_data_by_csv()
|
# test_import_binance_data_by_csv()
|
||||||
# batch_import_binance_data_by_csv()
|
batch_import_binance_data_by_csv()
|
||||||
# batch_update_volume_spike(threshold=2.0, is_us_stock=True)
|
# batch_update_volume_spike(threshold=2.0, is_us_stock=True)
|
||||||
# test_send_huge_volume_data_to_wechat()
|
# test_send_huge_volume_data_to_wechat()
|
||||||
# batch_initial_detect_volume_spike(threshold=2.0)
|
# batch_initial_detect_volume_spike(threshold=2.0)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
from core.trade.orb_trade import ORBStrategy
|
from core.trade.orb_trade import ORBStrategy
|
||||||
from config import US_STOCK_MONITOR_CONFIG, OKX_MONITOR_CONFIG
|
from config import US_STOCK_MONITOR_CONFIG, OKX_MONITOR_CONFIG, BINANCE_MONITOR_CONFIG
|
||||||
import core.logger as logging
|
import core.logger as logging
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
|
|
@ -12,12 +12,20 @@ logger = logging.logger
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
is_us_stock_list = [True, False]
|
# is_us_stock_list = [True, False]
|
||||||
|
is_us_stock_list = [False]
|
||||||
|
is_binance = True
|
||||||
bar = "5m"
|
bar = "5m"
|
||||||
direction_list = [None, "Long", "Short"]
|
direction_list = [None, "Long", "Short"]
|
||||||
by_sar_list = [False, True]
|
by_sar_list = [False, True]
|
||||||
price_range_mean_as_R_list = [False, True]
|
price_range_mean_as_R_list = [False, True]
|
||||||
by_big_k_list = [False, True]
|
by_big_k_list = [False, True]
|
||||||
|
by_1h_k_list = [False, True]
|
||||||
|
if is_binance:
|
||||||
|
start_date = BINANCE_MONITOR_CONFIG.get("volume_monitor", {}).get("initial_date", "2017-08-16 00:00:00")
|
||||||
|
if len(start_date) > 10:
|
||||||
|
start_date = start_date[:10]
|
||||||
|
else:
|
||||||
start_date = "2024-01-01"
|
start_date = "2024-01-01"
|
||||||
end_date = datetime.now().strftime("%Y-%m-%d")
|
end_date = datetime.now().strftime("%Y-%m-%d")
|
||||||
profit_target_multiple = 10
|
profit_target_multiple = 10
|
||||||
|
|
@ -34,10 +42,16 @@ def main():
|
||||||
for by_sar in by_sar_list:
|
for by_sar in by_sar_list:
|
||||||
for price_range_mean_as_R in price_range_mean_as_R_list:
|
for price_range_mean_as_R in price_range_mean_as_R_list:
|
||||||
for by_big_k in by_big_k_list:
|
for by_big_k in by_big_k_list:
|
||||||
|
for by_1h_k in by_1h_k_list:
|
||||||
if is_us_stock:
|
if is_us_stock:
|
||||||
symbols = US_STOCK_MONITOR_CONFIG.get("volume_monitor", {}).get(
|
symbols = US_STOCK_MONITOR_CONFIG.get("volume_monitor", {}).get(
|
||||||
"symbols", ["QQQ"]
|
"symbols", ["QQQ"]
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
if is_binance:
|
||||||
|
symbols = BINANCE_MONITOR_CONFIG.get("volume_monitor", {}).get(
|
||||||
|
"symbols", ["BTC-USDT"]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
symbols = OKX_MONITOR_CONFIG.get("volume_monitor", {}).get(
|
symbols = OKX_MONITOR_CONFIG.get("volume_monitor", {}).get(
|
||||||
"symbols", ["BTC-USDT"]
|
"symbols", ["BTC-USDT"]
|
||||||
|
|
@ -47,6 +61,7 @@ def main():
|
||||||
f"开始回测 {symbol}, 交易周期:{bar}, 开始日期:{start_date}, 结束日期:{end_date}, 是否是美股:{is_us_stock}, 交易方向:{direction}, 是否使用SAR:{by_sar}, 是否使用R为entry减stop:{price_range_mean_as_R}, 是否使用K线实体过50%:{by_big_k}"
|
f"开始回测 {symbol}, 交易周期:{bar}, 开始日期:{start_date}, 结束日期:{end_date}, 是否是美股:{is_us_stock}, 交易方向:{direction}, 是否使用SAR:{by_sar}, 是否使用R为entry减stop:{price_range_mean_as_R}, 是否使用K线实体过50%:{by_big_k}"
|
||||||
)
|
)
|
||||||
symbol_bar_data = None
|
symbol_bar_data = None
|
||||||
|
symbol_1h_data = None
|
||||||
found_symbol_bar_data = False
|
found_symbol_bar_data = False
|
||||||
for symbol_data_dict in symbol_data_cache:
|
for symbol_data_dict in symbol_data_cache:
|
||||||
if (
|
if (
|
||||||
|
|
@ -54,6 +69,7 @@ def main():
|
||||||
and symbol_data_dict["bar"] == bar
|
and symbol_data_dict["bar"] == bar
|
||||||
):
|
):
|
||||||
symbol_bar_data = symbol_data_dict["data"]
|
symbol_bar_data = symbol_data_dict["data"]
|
||||||
|
symbol_1h_data = symbol_data_dict["1h_data"]
|
||||||
found_symbol_bar_data = True
|
found_symbol_bar_data = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|
@ -63,6 +79,7 @@ def main():
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
end_date=end_date,
|
end_date=end_date,
|
||||||
is_us_stock=is_us_stock,
|
is_us_stock=is_us_stock,
|
||||||
|
is_binance=is_binance,
|
||||||
direction=direction,
|
direction=direction,
|
||||||
by_sar=by_sar,
|
by_sar=by_sar,
|
||||||
profit_target_multiple=profit_target_multiple,
|
profit_target_multiple=profit_target_multiple,
|
||||||
|
|
@ -71,15 +88,17 @@ def main():
|
||||||
risk_per_trade=risk_per_trade,
|
risk_per_trade=risk_per_trade,
|
||||||
commission_per_share=commission_per_share,
|
commission_per_share=commission_per_share,
|
||||||
symbol_bar_data=symbol_bar_data,
|
symbol_bar_data=symbol_bar_data,
|
||||||
|
symbol_1h_data=symbol_1h_data,
|
||||||
price_range_mean_as_R=price_range_mean_as_R,
|
price_range_mean_as_R=price_range_mean_as_R,
|
||||||
by_big_k=by_big_k,
|
by_big_k=by_big_k,
|
||||||
|
by_1h_k=by_1h_k,
|
||||||
)
|
)
|
||||||
symbol_bar_data, trades_df, trades_summary_df = orb_strategy.run()
|
symbol_bar_data, symbol_1h_data, trades_df, trades_summary_df = orb_strategy.run()
|
||||||
if symbol_bar_data is None or len(symbol_bar_data) == 0:
|
if symbol_bar_data is None or len(symbol_bar_data) == 0:
|
||||||
continue
|
continue
|
||||||
if not found_symbol_bar_data:
|
if not found_symbol_bar_data:
|
||||||
symbol_data_cache.append(
|
symbol_data_cache.append(
|
||||||
{"symbol": symbol, "bar": bar, "data": symbol_bar_data}
|
{"symbol": symbol, "bar": bar, "data": symbol_bar_data, "1h_data": symbol_1h_data}
|
||||||
)
|
)
|
||||||
if trades_summary_df is None or len(trades_summary_df) == 0:
|
if trades_summary_df is None or len(trades_summary_df) == 0:
|
||||||
continue
|
continue
|
||||||
|
|
@ -117,6 +136,9 @@ def main():
|
||||||
statitics_dict["max_total_return_record_df_K_count"].to_excel(
|
statitics_dict["max_total_return_record_df_K_count"].to_excel(
|
||||||
writer, sheet_name="最大总收益率_K线条件", index=False
|
writer, sheet_name="最大总收益率_K线条件", index=False
|
||||||
)
|
)
|
||||||
|
statitics_dict["max_total_return_record_df_1hK_count"].to_excel(
|
||||||
|
writer, sheet_name="最大总收益率_1小时K线条件", index=False
|
||||||
|
)
|
||||||
chart_path = r"./output/trade_sandbox/orb_strategy/chart/"
|
chart_path = r"./output/trade_sandbox/orb_strategy/chart/"
|
||||||
os.makedirs(chart_path, exist_ok=True)
|
os.makedirs(chart_path, exist_ok=True)
|
||||||
copy_chart_to_excel(chart_path, output_file_path)
|
copy_chart_to_excel(chart_path, output_file_path)
|
||||||
|
|
@ -170,6 +192,7 @@ def statistics_summary(trades_summary_df: pd.DataFrame):
|
||||||
summary["根据SAR"] = max_total_return_record["根据SAR"]
|
summary["根据SAR"] = max_total_return_record["根据SAR"]
|
||||||
summary["R算法"] = max_total_return_record["R算法"]
|
summary["R算法"] = max_total_return_record["R算法"]
|
||||||
summary["K线条件"] = max_total_return_record["K线条件"]
|
summary["K线条件"] = max_total_return_record["K线条件"]
|
||||||
|
summary["1小时K线条件"] = max_total_return_record["1小时K线条件"]
|
||||||
summary["总收益率%"] = max_total_return_record["总收益率%"]
|
summary["总收益率%"] = max_total_return_record["总收益率%"]
|
||||||
summary["自然收益率%"] = max_total_return_record["自然收益率%"]
|
summary["自然收益率%"] = max_total_return_record["自然收益率%"]
|
||||||
max_total_return_record_list.append(summary)
|
max_total_return_record_list.append(summary)
|
||||||
|
|
@ -192,14 +215,14 @@ def statistics_summary(trades_summary_df: pd.DataFrame):
|
||||||
# 其它(如Series、list、dict、ndarray等)转字符串
|
# 其它(如Series、list、dict、ndarray等)转字符串
|
||||||
return str(v)
|
return str(v)
|
||||||
|
|
||||||
for key_col in ["方向", "根据SAR", "R算法", "K线条件"]:
|
for key_col in ["方向", "根据SAR", "R算法", "K线条件", "1小时K线条件"]:
|
||||||
if key_col in max_total_return_record_df.columns:
|
if key_col in max_total_return_record_df.columns:
|
||||||
max_total_return_record_df[key_col] = max_total_return_record_df[
|
max_total_return_record_df[key_col] = max_total_return_record_df[
|
||||||
key_col
|
key_col
|
||||||
].apply(_to_hashable_scalar)
|
].apply(_to_hashable_scalar)
|
||||||
# 分组统计
|
# 分组统计
|
||||||
max_total_return_record_df_grouped_count = (
|
max_total_return_record_df_grouped_count = (
|
||||||
max_total_return_record_df.groupby(["方向", "根据SAR", "R算法", "K线条件"], dropna=False)
|
max_total_return_record_df.groupby(["方向", "根据SAR", "R算法", "K线条件", "1小时K线条件"], dropna=False)
|
||||||
.size()
|
.size()
|
||||||
.reset_index(name="数量")
|
.reset_index(name="数量")
|
||||||
)
|
)
|
||||||
|
|
@ -251,18 +274,27 @@ def statistics_summary(trades_summary_df: pd.DataFrame):
|
||||||
by="数量", ascending=False, inplace=True
|
by="数量", ascending=False, inplace=True
|
||||||
)
|
)
|
||||||
max_total_return_record_df_K_count.reset_index(drop=True, inplace=True)
|
max_total_return_record_df_K_count.reset_index(drop=True, inplace=True)
|
||||||
|
|
||||||
|
# 统计1小时K线条件的记录数目
|
||||||
|
max_total_return_record_df_1hK_count = (
|
||||||
|
max_total_return_record_df.groupby(["1小时K线条件"], dropna=False)
|
||||||
|
.size()
|
||||||
|
.reset_index(name="数量")
|
||||||
|
)
|
||||||
|
max_total_return_record_df_1hK_count.sort_values(
|
||||||
|
by="数量", ascending=False, inplace=True
|
||||||
|
)
|
||||||
|
max_total_return_record_df_1hK_count.reset_index(drop=True, inplace=True)
|
||||||
else:
|
else:
|
||||||
# 构造空结果,保证下游写入Excel不报错
|
# 构造空结果,保证下游写入Excel不报错
|
||||||
max_total_return_record_df_grouped_count = pd.DataFrame(
|
max_total_return_record_df_grouped_count = pd.DataFrame(
|
||||||
columns=["方向", "根据SAR", "R算法", "K线条件", "数量"]
|
columns=["方向", "根据SAR", "R算法", "K线条件", "1小时K线条件", "数量"]
|
||||||
)
|
|
||||||
max_total_return_record_df_direction_count = pd.DataFrame(
|
|
||||||
columns=["方向", "数量"]
|
|
||||||
)
|
)
|
||||||
|
max_total_return_record_df_direction_count = pd.DataFrame(columns=["方向", "数量"])
|
||||||
max_total_return_record_df_sar_count = pd.DataFrame(columns=["根据SAR", "数量"])
|
max_total_return_record_df_sar_count = pd.DataFrame(columns=["根据SAR", "数量"])
|
||||||
max_total_return_record_df_R_count = pd.DataFrame(columns=["R算法", "数量"])
|
max_total_return_record_df_R_count = pd.DataFrame(columns=["R算法", "数量"])
|
||||||
max_total_return_record_df_K_count = pd.DataFrame(columns=["K线条件", "数量"])
|
max_total_return_record_df_K_count = pd.DataFrame(columns=["K线条件", "数量"])
|
||||||
|
max_total_return_record_df_1hK_count = pd.DataFrame(columns=["1小时K线条件", "数量"])
|
||||||
result = {
|
result = {
|
||||||
"statistics_summary_df": statistics_summary_df,
|
"statistics_summary_df": statistics_summary_df,
|
||||||
"max_total_return_record_df": max_total_return_record_df,
|
"max_total_return_record_df": max_total_return_record_df,
|
||||||
|
|
@ -271,6 +303,7 @@ def statistics_summary(trades_summary_df: pd.DataFrame):
|
||||||
"max_total_return_record_df_sar_count": max_total_return_record_df_sar_count,
|
"max_total_return_record_df_sar_count": max_total_return_record_df_sar_count,
|
||||||
"max_total_return_record_df_R_count": max_total_return_record_df_R_count,
|
"max_total_return_record_df_R_count": max_total_return_record_df_R_count,
|
||||||
"max_total_return_record_df_K_count": max_total_return_record_df_K_count,
|
"max_total_return_record_df_K_count": max_total_return_record_df_K_count,
|
||||||
|
"max_total_return_record_df_1hK_count": max_total_return_record_df_1hK_count,
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
||||||
79
play.py
79
play.py
|
|
@ -2,6 +2,10 @@ import logging
|
||||||
from core.biz.quant_trader import QuantTrader
|
from core.biz.quant_trader import QuantTrader
|
||||||
from core.biz.strategy import QuantStrategy
|
from core.biz.strategy import QuantStrategy
|
||||||
|
|
||||||
|
from config import MYSQL_CONFIG
|
||||||
|
from sqlalchemy import create_engine, exc, text
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
|
|
@ -94,5 +98,78 @@ def main() -> None:
|
||||||
else:
|
else:
|
||||||
logging.warning("无效选择,请重新输入")
|
logging.warning("无效选择,请重新输入")
|
||||||
|
|
||||||
|
|
||||||
|
def test_query():
|
||||||
|
mysql_user = MYSQL_CONFIG.get("user", "xch")
|
||||||
|
mysql_password = MYSQL_CONFIG.get("password", "")
|
||||||
|
if not mysql_password:
|
||||||
|
raise ValueError("MySQL password is not set")
|
||||||
|
mysql_host = MYSQL_CONFIG.get("host", "localhost")
|
||||||
|
mysql_port = MYSQL_CONFIG.get("port", 3306)
|
||||||
|
mysql_database = MYSQL_CONFIG.get("database", "okx")
|
||||||
|
db_url = f"mysql+pymysql://{mysql_user}:{mysql_password}@{mysql_host}:{mysql_port}/{mysql_database}"
|
||||||
|
db_engine = create_engine(
|
||||||
|
db_url,
|
||||||
|
pool_size=25, # 连接池大小
|
||||||
|
max_overflow=10, # 允许的最大溢出连接
|
||||||
|
pool_timeout=30, # 连接超时时间(秒)
|
||||||
|
pool_recycle=60, # 连接回收时间(秒),避免长时间闲置
|
||||||
|
)
|
||||||
|
sql = "SELECT symbol, min(date_time), max(date_time) FROM okx.crypto_binance_data where bar='5m' group by symbol;"
|
||||||
|
condition_dict = {}
|
||||||
|
return_multi = True
|
||||||
|
try:
|
||||||
|
result = query_data(db_engine, sql, condition_dict, return_multi)
|
||||||
|
if result is not None and len(result) > 0:
|
||||||
|
data = pd.DataFrame(result)
|
||||||
|
data.columns = ["symbol", "min_date_time", "max_date_time"]
|
||||||
|
print(data)
|
||||||
|
# output to excel
|
||||||
|
data.to_excel("./data/binance/crypto_binance_data_5m.xlsx", index=False)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"查询数据出错: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def transform_data_type(data: dict):
|
||||||
|
"""
|
||||||
|
遍历字典,将所有Decimal类型的值转换为float类型
|
||||||
|
"""
|
||||||
|
from decimal import Decimal
|
||||||
|
for key, value in data.items():
|
||||||
|
if isinstance(value, Decimal):
|
||||||
|
data[key] = float(value)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def query_data(db_engine, sql: str, condition_dict: dict, return_multi: bool = True):
|
||||||
|
"""
|
||||||
|
查询数据
|
||||||
|
:param sql: 查询SQL
|
||||||
|
:param db_url: 数据库连接URL
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with db_engine.connect() as conn:
|
||||||
|
result = conn.execute(text(sql), condition_dict)
|
||||||
|
if return_multi:
|
||||||
|
result = result.fetchall()
|
||||||
|
if result:
|
||||||
|
result_list = [
|
||||||
|
transform_data_type(dict(row._mapping)) for row in result
|
||||||
|
]
|
||||||
|
return result_list
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
result = result.fetchone()
|
||||||
|
if result:
|
||||||
|
result_dict = transform_data_type(dict(result._mapping))
|
||||||
|
return result_dict
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"查询数据出错: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
# main()
|
||||||
|
test_query()
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,24 @@
|
||||||
|
use okx;
|
||||||
|
|
||||||
|
select DISTINCT symbol from crypto_huge_volume
|
||||||
|
where symbol in ("QQQ", "TQQQ", "MSFT", "AAPL", "GOOG", "NVDA", "META", "AMZN", "TSLA", "AVGO") and bar="30m"
|
||||||
|
and window_size=120
|
||||||
|
group by symbol
|
||||||
|
|
||||||
select * from crypto_market_data
|
select * from crypto_market_data
|
||||||
where symbol = "TQQQ" and bar="5m"
|
where symbol="PLTR" and bar="5m"
|
||||||
order by date_time_us DESC;
|
order by date_time_us
|
||||||
|
|
||||||
select * from crypto_huge_volume
|
|
||||||
where symbol = "QQQ" and bar="5m"
|
|
||||||
order by date_time;
|
|
||||||
|
|
||||||
|
select * from crypto_market_data
|
||||||
|
where symbol = "BTC-USDT" and bar="1H" #and open between 114271 and 114272 # AND date_time > "2025-08-21"
|
||||||
|
order by date_time desc;
|
||||||
|
|
||||||
|
select count(1) from crypto_market_data where
|
||||||
|
symbol in ("QQQ", "TQQQ", "MSFT", "AAPL", "GOOG", "NVDA", "META", "AMZN", "TSLA", "AVGO")
|
||||||
|
|
||||||
|
select count(1) from crypto_huge_volume where
|
||||||
|
symbol in ("QQQ", "TQQQ", "MSFT", "AAPL", "GOOG", "NVDA", "META", "AMZN", "TSLA", "AVGO")
|
||||||
|
|
||||||
|
|
||||||
select symbol, bar, date_time, close,
|
select symbol, bar, date_time, close,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
-- crypto_binance_data表核心索引优化脚本
|
||||||
|
-- 针对500万+数据量的关键查询性能优化
|
||||||
|
-- 基于实际代码中的查询模式分析
|
||||||
|
|
||||||
|
-- 核心索引1: 主要查询模式索引
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? ORDER BY timestamp
|
||||||
|
-- 这是代码中最常见的查询模式
|
||||||
|
CREATE INDEX idx_symbol_bar_timestamp ON crypto_binance_data (symbol, bar, timestamp);
|
||||||
|
|
||||||
|
-- 核心索引2: 时间范围查询索引
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND timestamp BETWEEN ? AND ?
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND timestamp >= ? AND timestamp <= ?
|
||||||
|
# CREATE INDEX idx_symbol_bar_timestamp_range ON crypto_binance_data (symbol, bar, timestamp);
|
||||||
|
|
||||||
|
-- 核心索引3: 分组查询索引
|
||||||
|
-- 覆盖: GROUP BY symbol, bar
|
||||||
|
-- 覆盖: SELECT symbol, min(date_time), max(date_time) FROM crypto_binance_data WHERE bar='5m' GROUP BY symbol
|
||||||
|
CREATE INDEX idx_bar_symbol ON crypto_binance_data (bar, symbol);
|
||||||
|
|
||||||
|
-- 核心索引4: 技术指标查询索引
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND macd_signal = ?
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND kdj_signal = ?
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND rsi_signal = ?
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND boll_signal = ?
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND ma_cross = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_indicators ON crypto_binance_data (symbol, bar, macd_signal, kdj_signal, rsi_signal, boll_signal, ma_cross);
|
||||||
|
|
||||||
|
-- 核心索引5: 时间戳排序索引
|
||||||
|
-- 覆盖: ORDER BY timestamp DESC/ASC
|
||||||
|
-- 优化: 查询最新数据 ORDER BY timestamp DESC LIMIT 1
|
||||||
|
CREATE INDEX idx_timestamp_desc ON crypto_binance_data (timestamp DESC);
|
||||||
|
|
||||||
|
-- 核心索引6: 日期时间查询索引
|
||||||
|
-- 覆盖: WHERE date_time >= ? AND date_time <= ?
|
||||||
|
-- 覆盖: ORDER BY date_time
|
||||||
|
CREATE INDEX idx_date_time ON crypto_binance_data (date_time);
|
||||||
|
|
||||||
|
-- 核心索引7: 成交量查询索引
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND volume > ?
|
||||||
|
-- 覆盖: ORDER BY volume DESC
|
||||||
|
CREATE INDEX idx_symbol_bar_volume ON crypto_binance_data (symbol, bar, volume);
|
||||||
|
|
||||||
|
-- 核心索引8: 价格查询索引
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND close > ? AND close < ?
|
||||||
|
-- 覆盖: ORDER BY close DESC/ASC
|
||||||
|
CREATE INDEX idx_symbol_bar_close ON crypto_binance_data (symbol, bar, close);
|
||||||
|
|
||||||
|
-- 执行前检查现有索引
|
||||||
|
-- SHOW INDEX FROM crypto_binance_data;
|
||||||
|
|
||||||
|
-- 执行后验证索引效果
|
||||||
|
-- EXPLAIN SELECT * FROM crypto_binance_data WHERE symbol = 'BTC-USDT' AND bar = '5m' ORDER BY timestamp DESC LIMIT 100;
|
||||||
|
-- EXPLAIN SELECT symbol, min(date_time), max(date_time) FROM crypto_binance_data WHERE bar='5m' GROUP BY symbol;
|
||||||
|
-- EXPLAIN SELECT * FROM crypto_binance_data WHERE symbol = 'BTC-USDT' AND bar = '5m' AND timestamp BETWEEN 1640995200000 AND 1641081600000;
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
-- crypto_binance_data表索引优化脚本
|
||||||
|
-- 针对500万+数据量的查询性能优化
|
||||||
|
|
||||||
|
-- 1. 主要查询索引 - 覆盖最常见的查询模式
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? ORDER BY timestamp
|
||||||
|
CREATE INDEX idx_symbol_bar_timestamp ON crypto_binance_data (symbol, bar, timestamp);
|
||||||
|
|
||||||
|
-- 2. 时间范围查询索引 - 优化时间范围查询
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND timestamp BETWEEN ? AND ?
|
||||||
|
CREATE INDEX idx_symbol_bar_timestamp_range ON crypto_binance_data (symbol, bar, timestamp);
|
||||||
|
|
||||||
|
-- 3. 按symbol分组查询索引 - 优化GROUP BY symbol查询
|
||||||
|
-- 支持: GROUP BY symbol, bar
|
||||||
|
CREATE INDEX idx_symbol_bar ON crypto_binance_data (symbol, bar);
|
||||||
|
|
||||||
|
-- 4. 时间戳排序索引 - 优化ORDER BY timestamp查询
|
||||||
|
-- 支持: ORDER BY timestamp DESC/ASC
|
||||||
|
CREATE INDEX idx_timestamp ON crypto_binance_data (timestamp);
|
||||||
|
|
||||||
|
-- 5. 技术指标查询索引 - 优化技术指标相关查询
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND macd_signal = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_macd_signal ON crypto_binance_data (symbol, bar, macd_signal);
|
||||||
|
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND kdj_signal = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_kdj_signal ON crypto_binance_data (symbol, bar, kdj_signal);
|
||||||
|
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND rsi_signal = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_rsi_signal ON crypto_binance_data (symbol, bar, rsi_signal);
|
||||||
|
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND boll_signal = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_boll_signal ON crypto_binance_data (symbol, bar, boll_signal);
|
||||||
|
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND ma_cross = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_ma_cross ON crypto_binance_data (symbol, bar, ma_cross);
|
||||||
|
|
||||||
|
-- 6. 复合技术指标查询索引 - 优化多条件技术指标查询
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND macd_signal = ? AND kdj_signal = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_macd_kdj ON crypto_binance_data (symbol, bar, macd_signal, kdj_signal);
|
||||||
|
|
||||||
|
-- 7. 日期时间查询索引 - 优化按日期时间查询
|
||||||
|
-- 支持: WHERE date_time >= ? AND date_time <= ?
|
||||||
|
CREATE INDEX idx_date_time ON crypto_binance_data (date_time);
|
||||||
|
|
||||||
|
-- 8. 交易对时间索引 - 优化特定交易对的时间查询
|
||||||
|
-- 支持: WHERE symbol = ? ORDER BY timestamp
|
||||||
|
CREATE INDEX idx_symbol_timestamp ON crypto_binance_data (symbol, timestamp);
|
||||||
|
|
||||||
|
-- 9. 周期时间索引 - 优化特定周期的时间查询
|
||||||
|
-- 支持: WHERE bar = ? ORDER BY timestamp
|
||||||
|
CREATE INDEX idx_bar_timestamp ON crypto_binance_data (bar, timestamp);
|
||||||
|
|
||||||
|
-- 10. 统计查询优化索引 - 优化COUNT, AVG等聚合查询
|
||||||
|
-- 支持: SELECT COUNT(*) FROM crypto_binance_data WHERE symbol = ? AND bar = ?
|
||||||
|
CREATE INDEX idx_symbol_bar_volume ON crypto_binance_data (symbol, bar, volume);
|
||||||
|
|
||||||
|
-- 11. 价格相关查询索引 - 优化价格相关查询
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND close > ? AND close < ?
|
||||||
|
CREATE INDEX idx_symbol_bar_close ON crypto_binance_data (symbol, bar, close);
|
||||||
|
|
||||||
|
-- 12. 成交量相关查询索引 - 优化成交量相关查询
|
||||||
|
-- 支持: WHERE symbol = ? AND bar = ? AND volume > ?
|
||||||
|
CREATE INDEX idx_symbol_bar_volume_high ON crypto_binance_data (symbol, bar, volume);
|
||||||
|
|
||||||
|
-- 删除可能存在的重复或低效索引
|
||||||
|
-- 注意:在实际执行前请先检查现有索引,避免删除正在使用的索引
|
||||||
|
|
||||||
|
-- 查看当前索引状态
|
||||||
|
-- SHOW INDEX FROM crypto_binance_data;
|
||||||
|
|
||||||
|
-- 分析查询性能
|
||||||
|
-- EXPLAIN SELECT * FROM crypto_binance_data WHERE symbol = 'BTC-USDT' AND bar = '5m' ORDER BY timestamp DESC LIMIT 100;
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
-- crypto_binance_huge_volume表核心索引优化脚本
|
||||||
|
-- 针对2000万+数据量的关键查询性能优化
|
||||||
|
-- 基于实际代码中的查询模式分析
|
||||||
|
|
||||||
|
-- 核心索引1: 主要查询模式索引
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND window_size = ? ORDER BY timestamp
|
||||||
|
-- 这是代码中最常见的查询模式,用于获取特定交易对、周期、窗口大小的数据
|
||||||
|
CREATE INDEX idx_symbol_bar_window_size_timestamp ON crypto_binance_huge_volume (symbol, bar, window_size, timestamp);
|
||||||
|
|
||||||
|
-- 核心索引2: 时间范围查询索引
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND window_size = ? AND timestamp BETWEEN ? AND ?
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND window_size = ? AND timestamp >= ? AND timestamp <= ?
|
||||||
|
# CREATE INDEX idx_symbol_bar_window_size_timestamp_range ON crypto_binance_huge_volume (symbol, bar, window_size, timestamp);
|
||||||
|
|
||||||
|
-- 核心索引3: 巨量交易查询索引
|
||||||
|
-- 覆盖: WHERE huge_volume = 1
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND window_size = ? AND huge_volume = 1
|
||||||
|
CREATE INDEX idx_huge_volume_symbol_bar_window_size ON crypto_binance_huge_volume (huge_volume, symbol, bar, window_size);
|
||||||
|
|
||||||
|
-- 核心索引4: 成交量比率排序索引
|
||||||
|
-- 覆盖: ORDER BY volume_ratio DESC
|
||||||
|
-- 覆盖: WHERE huge_volume = 1 ORDER BY volume_ratio DESC
|
||||||
|
CREATE INDEX idx_volume_ratio_desc ON crypto_binance_huge_volume (volume_ratio DESC);
|
||||||
|
|
||||||
|
-- 核心索引5: 价格分位数高点查询索引
|
||||||
|
-- 覆盖: WHERE close_80_high = 1, close_90_high = 1
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND window_size = ? AND close_80_high = 1
|
||||||
|
CREATE INDEX idx_close_80_90_high ON crypto_binance_huge_volume (close_80_high, close_90_high, symbol, bar, window_size);
|
||||||
|
|
||||||
|
-- 核心索引6: 价格分位数低点查询索引
|
||||||
|
-- 覆盖: WHERE close_20_low = 1, close_10_low = 1
|
||||||
|
-- 覆盖: WHERE symbol = ? AND bar = ? AND window_size = ? AND close_20_low = 1
|
||||||
|
CREATE INDEX idx_close_20_10_low ON crypto_binance_huge_volume (close_20_low, close_10_low, symbol, bar, window_size);
|
||||||
|
|
||||||
|
-- 核心索引7: 最高价分位数查询索引
|
||||||
|
-- 覆盖: WHERE high_80_high = 1, high_90_high = 1, high_20_low = 1, high_10_low = 1
|
||||||
|
CREATE INDEX idx_high_percentiles ON crypto_binance_huge_volume (high_80_high, high_90_high, high_20_low, high_10_low, symbol, bar, window_size);
|
||||||
|
|
||||||
|
-- 核心索引8: 最低价分位数查询索引
|
||||||
|
-- 覆盖: WHERE low_80_high = 1, low_90_high = 1, low_20_low = 1, low_10_low = 1
|
||||||
|
CREATE INDEX idx_low_percentiles ON crypto_binance_huge_volume (low_80_high, low_90_high, low_20_low, low_10_low, symbol, bar, window_size);
|
||||||
|
|
||||||
|
-- 核心索引9: 复合量价尖峰查询索引
|
||||||
|
-- 覆盖: WHERE huge_volume = 1 AND (close_80_high = 1 OR close_20_low = 1)
|
||||||
|
-- 覆盖: WHERE huge_volume = 1 AND (close_90_high = 1 OR close_10_low = 1)
|
||||||
|
CREATE INDEX idx_huge_volume_price_spike ON crypto_binance_huge_volume (huge_volume, close_80_high, close_20_low, close_90_high, close_10_low, symbol, bar, window_size);
|
||||||
|
|
||||||
|
-- 核心索引10: 分组统计查询索引
|
||||||
|
-- 覆盖: GROUP BY symbol, bar, window_size
|
||||||
|
-- 覆盖: GROUP BY symbol, bar
|
||||||
|
CREATE INDEX idx_symbol_bar_window_size_group ON crypto_binance_huge_volume (symbol, bar, window_size);
|
||||||
|
|
||||||
|
-- 核心索引11: 时间戳排序索引
|
||||||
|
-- 覆盖: ORDER BY timestamp DESC/ASC
|
||||||
|
-- 优化: 查询最新数据 ORDER BY timestamp DESC LIMIT 1
|
||||||
|
CREATE INDEX idx_timestamp_desc ON crypto_binance_huge_volume (timestamp DESC);
|
||||||
|
|
||||||
|
-- 核心索引12: 日期时间查询索引
|
||||||
|
-- 覆盖: WHERE date_time >= ? AND date_time <= ?
|
||||||
|
-- 覆盖: ORDER BY date_time
|
||||||
|
CREATE INDEX idx_date_time ON crypto_binance_huge_volume (date_time);
|
||||||
|
|
||||||
|
-- 执行前检查现有索引
|
||||||
|
-- SHOW INDEX FROM crypto_binance_huge_volume;
|
||||||
|
|
||||||
|
-- 执行后验证索引效果
|
||||||
|
-- EXPLAIN SELECT * FROM crypto_binance_huge_volume WHERE symbol = 'BTC-USDT' AND bar = '5m' AND window_size = 50 ORDER BY timestamp DESC LIMIT 100;
|
||||||
|
-- EXPLAIN SELECT * FROM crypto_binance_huge_volume WHERE huge_volume = 1 AND close_80_high = 1 ORDER BY volume_ratio DESC LIMIT 10;
|
||||||
|
-- EXPLAIN SELECT COUNT(*) FROM crypto_binance_huge_volume WHERE symbol = 'BTC-USDT' AND bar = '5m' AND window_size = 50;
|
||||||
Loading…
Reference in New Issue