optimize orb strategy

This commit is contained in:
blade 2025-09-03 18:26:27 +08:00
parent f79834647d
commit 644df3a42f
5 changed files with 181 additions and 85 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
core/db/__pycache__/*.pyc core/db/__pycache__/*.pyc
core/biz/__pycache__/*.pyc core/biz/__pycache__/*.pyc
core/statistics/__pycache__/*.pyc core/statistics/__pycache__/*.pyc
/data

View File

View File

@ -38,6 +38,8 @@ class ORBStrategy:
direction=None, direction=None,
by_sar=False, by_sar=False,
symbol_bar_data=None, symbol_bar_data=None,
price_range_mean_as_R=False,
by_big_k=False,
): ):
""" """
初始化ORB策略参数 初始化ORB策略参数
@ -63,6 +65,8 @@ class ORBStrategy:
:param is_us_stock: 是否是美股 :param is_us_stock: 是否是美股
:param direction: 方向None=自动Long=多头Short=空头 :param direction: 方向None=自动Long=多头Short=空头
:param by_sar: 是否根据SAR指标生成信号True=False= :param by_sar: 是否根据SAR指标生成信号True=False=
:param price_range_mean_as_R: 是否将价格振幅均值作为$RTrue=False=
:param by_big_k: 是否根据K线实体部分亦即abs(open-close)超过high-low的50%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}"
@ -106,6 +110,17 @@ 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.price_range_mean_as_R = price_range_mean_as_R
if self.price_range_mean_as_R:
self.price_range_mean_as_R_desc = "R为振幅均值"
else:
self.price_range_mean_as_R_desc = "R为entry减stop"
self.by_big_k = by_big_k
if self.by_big_k:
self.by_big_k_desc = "K线实体过50%"
else:
self.by_big_k_desc = "无K线要求"
def run(self): def run(self):
""" """
@ -127,7 +142,9 @@ class ORBStrategy:
:param end_date: 结束日期格式YYYY-MM-DD :param end_date: 结束日期格式YYYY-MM-DD
:param interval: K线周期默认5分钟 :param interval: K线周期默认5分钟
""" """
logger.info(f"开始获取{self.symbol}数据:{self.start_date}{self.end_date},间隔{self.bar}") logger.info(
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( data = self.db_market_data.query_market_data_by_symbol_bar(
self.symbol, self.bar, start=self.start_date, end=self.end_date self.symbol, self.bar, start=self.start_date, end=self.end_date
@ -153,7 +170,6 @@ class ORBStrategy:
self.start_date = data["Date"].min().strftime("%Y-%m-%d") self.start_date = data["Date"].min().strftime("%Y-%m-%d")
# 最大data["Date"] # 最大data["Date"]
self.end_date = data["Date"].max().strftime("%Y-%m-%d") self.end_date = data["Date"].max().strftime("%Y-%m-%d")
self.data = data[ self.data = data[
[ [
"symbol", "symbol",
@ -169,6 +185,7 @@ class ORBStrategy:
] ]
].copy() ].copy()
self.data.rename(columns={date_time_field: "date_time"}, inplace=True) self.data.rename(columns={date_time_field: "date_time"}, inplace=True)
self.calculate_price_range_mean()
self.symbol_bar_data = self.data.copy() self.symbol_bar_data = self.data.copy()
else: else:
self.data = self.symbol_bar_data.copy() self.data = self.symbol_bar_data.copy()
@ -189,21 +206,24 @@ class ORBStrategy:
pass pass
logger.info(f"收盘价均值:{self.close_mean}") logger.info(f"收盘价均值:{self.close_mean}")
logger.info(f"初始资金调整为:{self.initial_capital}") logger.info(f"初始资金调整为:{self.initial_capital}")
logger.info(f"成功获取{self.symbol}数据:{len(self.data)}{self.bar}K线,开始日期={self.start_date},结束日期={self.end_date}") logger.info(
f"成功获取{self.symbol}数据:{len(self.data)}{self.bar}K线,开始日期={self.start_date},结束日期={self.end_date}"
)
def calculate_shares(self, account_value, entry_price, stop_price): def calculate_shares(self, account_value, entry_price, stop_price, risk_assumed):
""" """
根据ORB公式计算交易股数 根据ORB公式计算交易股数
:param account_value: 当前账户价值美元 :param account_value: 当前账户价值美元
:param entry_price: 交易entry价格第二根5分钟K线开盘价 :param entry_price: 交易entry价格第二根5分钟K线开盘价
:param stop_price: 止损价格多头=第一根K线最低价空头=第一根K线最高价 :param stop_price: 止损价格多头=第一根K线最低价空头=第一根K线最高价
:param risk_assumed: 风险金额$R,根据price_range_mean_as_R决定
:return: 整数股数Shares :return: 整数股数Shares
""" """
logger.info( logger.info(
f"开始计算交易股数:账户价值={account_value}entry价格={entry_price},止损价格={stop_price}" f"开始计算交易股数:账户价值={account_value}entry价格={entry_price},止损价格={stop_price}"
) )
# 计算单交易风险金额($R # 计算单交易风险金额($R
risk_per_trade_dollar = abs(entry_price - stop_price) # 风险金额取绝对值 risk_per_trade_dollar = risk_assumed # 风险金额取绝对值
if risk_per_trade_dollar <= 0: if risk_per_trade_dollar <= 0:
return 0 # 无风险时不交易 return 0 # 无风险时不交易
@ -247,6 +267,12 @@ class ORBStrategy:
close1 = first_candle["Close"] close1 = first_candle["Close"]
sar_signal = first_candle["sar_signal"] sar_signal = first_candle["sar_signal"]
if high1 == low1:
continue
if self.by_big_k:
if (abs(open1 - close1) / (high1 - low1)) < 0.5:
continue
# 第二根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线开盘价
@ -303,6 +329,17 @@ class ORBStrategy:
f"生成信号完成:共{len(signals_df)}个交易日,其中多头{sum(signals_df['Signal']=='Long')}次,空头{sum(signals_df['Signal']=='Short')}" f"生成信号完成:共{len(signals_df)}个交易日,其中多头{sum(signals_df['Signal']=='Long')}次,空头{sum(signals_df['Signal']=='Short')}"
) )
def calculate_price_range_mean(self):
"""
计算价格振幅均值振幅为最高价与最低价之差
计算价格振幅标准差
要求用滑动窗口: window_size=100计算均值每次计算都包含当前行
返回一个新列列名为"PriceRangeMean"
"""
self.data["PriceRange"] = self.data["High"] - self.data["Low"]
self.data["PriceRangeMean"] = self.data["PriceRange"].rolling(window=100).mean()
self.data["PriceRangeStd"] = self.data["PriceRange"].rolling(window=100).std()
def backtest(self): def backtest(self):
""" """
回测ORB策略 回测ORB策略
@ -343,7 +380,18 @@ class ORBStrategy:
stop_price = signal_row["StopPrice"] stop_price = signal_row["StopPrice"]
high1 = signal_row["High1"] high1 = signal_row["High1"]
low1 = signal_row["Low1"] low1 = signal_row["Low1"]
risk_assumed = abs(entry_price - stop_price) # 计算$R price_range = signal_row["PriceRange"]
price_range_mean = signal_row["PriceRangeMean"]
price_range_std = signal_row["PriceRangeStd"]
# 计算$R
if (
self.price_range_mean_as_R
and price_range_mean is not None
and price_range_mean > 0
):
risk_assumed = price_range_mean
else:
risk_assumed = abs(entry_price - stop_price)
profit_target = ( profit_target = (
entry_price + (risk_assumed * self.profit_target_multiple) entry_price + (risk_assumed * self.profit_target_multiple)
if signal == "Long" if signal == "Long"
@ -351,7 +399,9 @@ class ORBStrategy:
) )
# 计算交易股数 # 计算交易股数
shares = self.calculate_shares(account_value, entry_price, stop_price) shares = self.calculate_shares(
account_value, entry_price, stop_price, risk_assumed
)
if shares == 0: if shares == 0:
# 股数为0→不交易 # 股数为0→不交易
equity_history.append(account_value) equity_history.append(account_value)
@ -425,6 +475,8 @@ class ORBStrategy:
"TradeID": trade_id, "TradeID": trade_id,
"Direction": self.direction_desc, "Direction": self.direction_desc,
"BySar": self.sar_desc, "BySar": self.sar_desc,
"PriceRangeMeanAsR": self.price_range_mean_as_R_desc,
"ByBigK": self.by_big_k_desc,
"Symbol": self.symbol, "Symbol": self.symbol,
"Bar": self.bar, "Bar": self.bar,
"Date": date, "Date": date,
@ -467,8 +519,12 @@ class ORBStrategy:
else 0 else 0
) )
# 计算盈亏比 # 计算盈亏比
profit_sum = self.trades_df[self.trades_df["ProfitLoss"] > 0]["ProfitLoss"].sum() profit_sum = self.trades_df[self.trades_df["ProfitLoss"] > 0][
loss_sum = abs(self.trades_df[self.trades_df["ProfitLoss"] < 0]["ProfitLoss"].sum()) "ProfitLoss"
].sum()
loss_sum = abs(
self.trades_df[self.trades_df["ProfitLoss"] < 0]["ProfitLoss"].sum()
)
if loss_sum == 0: if loss_sum == 0:
profit_loss_ratio = float("inf") profit_loss_ratio = float("inf")
else: else:
@ -504,7 +560,9 @@ class ORBStrategy:
logger.info(f"最大单笔盈利:${self.trades_df['ProfitLoss'].max():.2f}") logger.info(f"最大单笔盈利:${self.trades_df['ProfitLoss'].max():.2f}")
self.trades_summary["最大单笔盈利$"] = self.trades_df["ProfitLoss"].max() self.trades_summary["最大单笔盈利$"] = self.trades_df["ProfitLoss"].max()
logger.info(f"最大单笔亏损:${abs(self.trades_df['ProfitLoss'].min()):.2f}") logger.info(f"最大单笔亏损:${abs(self.trades_df['ProfitLoss'].min()):.2f}")
self.trades_summary["最大单笔亏损$"] = abs(self.trades_df["ProfitLoss"].min()) self.trades_summary["最大单笔亏损$"] = abs(
self.trades_df["ProfitLoss"].min()
)
else: else:
logger.info("没有交易") logger.info("没有交易")
self.trades_summary_df = pd.DataFrame([self.trades_summary]) self.trades_summary_df = pd.DataFrame([self.trades_summary])
@ -516,6 +574,8 @@ class ORBStrategy:
self.trades_summary = {} self.trades_summary = {}
self.trades_summary["方向"] = self.direction_desc self.trades_summary["方向"] = self.direction_desc
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["K线条件"] = self.by_big_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
@ -532,7 +592,6 @@ class ORBStrategy:
self.trades_summary["最大单笔盈利$"] = 0 self.trades_summary["最大单笔盈利$"] = 0
self.trades_summary["最大单笔亏损$"] = 0 self.trades_summary["最大单笔亏损$"] = 0
def create_equity_curve(self): def create_equity_curve(self):
""" """
创建账户净值曲线 创建账户净值曲线
@ -600,7 +659,11 @@ class ORBStrategy:
marker="s", marker="s",
markersize=4, markersize=4,
) )
plt.title(f"ORB曲线 {symbol} {bar} {self.direction_desc} {self.sar_desc}", fontsize=14, fontweight="bold") plt.title(
f"{symbol} {bar} {self.direction_desc} {self.sar_desc} {self.price_range_mean_as_R_desc} {self.by_big_k_desc}",
fontsize=14,
fontweight="bold",
)
plt.xlabel("时间", fontsize=12) plt.xlabel("时间", fontsize=12)
plt.ylabel("涨跌变化", fontsize=12) plt.ylabel("涨跌变化", fontsize=12)
plt.legend(fontsize=11) plt.legend(fontsize=11)
@ -641,9 +704,7 @@ class ORBStrategy:
fontsize=10, fontsize=10,
) )
plt.tight_layout() plt.tight_layout()
self.chart_save_path = ( 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"
f"{self.output_chart_folder}/{symbol}_{bar}_{self.direction_desc}_{self.sar_desc}_orb_strategy_equity_curve.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()
@ -653,16 +714,14 @@ 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}.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}.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:
self.trades_df.to_excel(writer, sheet_name="交易明细", index=False) self.trades_df.to_excel(writer, sheet_name="交易明细", index=False)
self.trades_summary_df.to_excel(writer, sheet_name="交易总结", index=False) self.trades_summary_df.to_excel(writer, sheet_name="交易总结", index=False)
if os.path.exists(self.chart_save_path): if os.path.exists(self.chart_save_path):
charts_dict = { charts_dict = {"账户净值曲线": self.chart_save_path}
"账户净值曲线": self.chart_save_path
}
self.output_chart_to_excel(output_file_path, charts_dict) self.output_chart_to_excel(output_file_path, charts_dict)
def output_chart_to_excel(self, excel_file_path: str, charts_dict: dict): def output_chart_to_excel(self, excel_file_path: str, charts_dict: dict):

View File

@ -16,6 +16,8 @@ def main():
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]
by_big_k_list = [False, True]
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
@ -30,55 +32,59 @@ def main():
for is_us_stock in is_us_stock_list: for is_us_stock in is_us_stock_list:
for direction in direction_list: for direction in direction_list:
for by_sar in by_sar_list: for by_sar in by_sar_list:
if is_us_stock: for price_range_mean_as_R in price_range_mean_as_R_list:
symbols = US_STOCK_MONITOR_CONFIG.get("volume_monitor", {}).get( for by_big_k in by_big_k_list:
"symbols", ["QQQ"] if is_us_stock:
) symbols = US_STOCK_MONITOR_CONFIG.get("volume_monitor", {}).get(
else: "symbols", ["QQQ"]
symbols = OKX_MONITOR_CONFIG.get("volume_monitor", {}).get( )
"symbols", ["BTC-USDT"] else:
) symbols = OKX_MONITOR_CONFIG.get("volume_monitor", {}).get(
for symbol in symbols: "symbols", ["BTC-USDT"]
logger.info( )
f"开始回测 {symbol}, 交易周期:{bar}, 开始日期:{start_date}, 结束日期:{end_date}, 是否是美股:{is_us_stock}, 交易方向:{direction}, 是否使用SAR:{by_sar}" for symbol in symbols:
) logger.info(
symbol_bar_data = None 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}"
found_symbol_bar_data = False )
for symbol_data_dict in symbol_data_cache: symbol_bar_data = None
if ( found_symbol_bar_data = False
symbol_data_dict["symbol"] == symbol for symbol_data_dict in symbol_data_cache:
and symbol_data_dict["bar"] == bar if (
): symbol_data_dict["symbol"] == symbol
symbol_bar_data = symbol_data_dict["data"] and symbol_data_dict["bar"] == bar
found_symbol_bar_data = True ):
break symbol_bar_data = symbol_data_dict["data"]
found_symbol_bar_data = True
break
orb_strategy = ORBStrategy( orb_strategy = ORBStrategy(
symbol=symbol, symbol=symbol,
bar=bar, bar=bar,
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,
direction=direction, direction=direction,
by_sar=by_sar, by_sar=by_sar,
profit_target_multiple=profit_target_multiple, profit_target_multiple=profit_target_multiple,
initial_capital=initial_capital, initial_capital=initial_capital,
max_leverage=max_leverage, max_leverage=max_leverage,
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,
) price_range_mean_as_R=price_range_mean_as_R,
symbol_bar_data, trades_df, trades_summary_df = orb_strategy.run() by_big_k=by_big_k,
if symbol_bar_data is None or len(symbol_bar_data) == 0: )
continue symbol_bar_data, trades_df, trades_summary_df = orb_strategy.run()
if not found_symbol_bar_data: if symbol_bar_data is None or len(symbol_bar_data) == 0:
symbol_data_cache.append( continue
{"symbol": symbol, "bar": bar, "data": symbol_bar_data} if not found_symbol_bar_data:
) symbol_data_cache.append(
if trades_summary_df is None or len(trades_summary_df) == 0: {"symbol": symbol, "bar": bar, "data": symbol_bar_data}
continue )
trades_summary_df_list.append(trades_summary_df) if trades_summary_df is None or len(trades_summary_df) == 0:
trades_df_list.append(trades_df) continue
trades_summary_df_list.append(trades_summary_df)
trades_df_list.append(trades_df)
total_trades_df = pd.concat(trades_df_list) total_trades_df = pd.concat(trades_df_list)
total_trades_summary_df = pd.concat(trades_summary_df_list) total_trades_summary_df = pd.concat(trades_summary_df_list)
statitics_dict = statistics_summary(total_trades_summary_df) statitics_dict = statistics_summary(total_trades_summary_df)
@ -97,13 +103,19 @@ def main():
writer, sheet_name="最大总收益率记录", index=False writer, sheet_name="最大总收益率记录", index=False
) )
statitics_dict["max_total_return_record_df_grouped_count"].to_excel( statitics_dict["max_total_return_record_df_grouped_count"].to_excel(
writer, sheet_name="最大总收益率记录_方向和根据SAR的组合", index=False writer, sheet_name="最大总收益率_所有条件组合", index=False
) )
statitics_dict["max_total_return_record_df_direction_count"].to_excel( statitics_dict["max_total_return_record_df_direction_count"].to_excel(
writer, sheet_name="最大总收益率记录_方向", index=False writer, sheet_name="最大总收益率_方向", index=False
) )
statitics_dict["max_total_return_record_df_sar_count"].to_excel( statitics_dict["max_total_return_record_df_sar_count"].to_excel(
writer, sheet_name="最大总收益率记录_根据SAR", index=False writer, sheet_name="最大总收益率_根据SAR", index=False
)
statitics_dict["max_total_return_record_df_R_count"].to_excel(
writer, sheet_name="最大总收益率_R算法", index=False
)
statitics_dict["max_total_return_record_df_K_count"].to_excel(
writer, sheet_name="最大总收益率_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)
@ -156,6 +168,8 @@ def statistics_summary(trades_summary_df: pd.DataFrame):
summary["股票代码"] = symbol summary["股票代码"] = symbol
summary["方向"] = max_total_return_record["方向"] summary["方向"] = max_total_return_record["方向"]
summary["根据SAR"] = max_total_return_record["根据SAR"] summary["根据SAR"] = max_total_return_record["根据SAR"]
summary["R算法"] = max_total_return_record["R算法"]
summary["K线条件"] = max_total_return_record["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)
@ -178,14 +192,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"]: for key_col in ["方向", "根据SAR", "R算法", "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"], dropna=False) max_total_return_record_df.groupby(["方向", "根据SAR", "R算法", "K线条件"], dropna=False)
.size() .size()
.reset_index(name="数量") .reset_index(name="数量")
) )
@ -215,15 +229,39 @@ def statistics_summary(trades_summary_df: pd.DataFrame):
by="数量", ascending=False, inplace=True by="数量", ascending=False, inplace=True
) )
max_total_return_record_df_sar_count.reset_index(drop=True, inplace=True) max_total_return_record_df_sar_count.reset_index(drop=True, inplace=True)
# 统计R算法的记录数目
max_total_return_record_df_R_count = (
max_total_return_record_df.groupby(["R算法"], dropna=False)
.size()
.reset_index(name="数量")
)
max_total_return_record_df_R_count.sort_values(
by="数量", ascending=False, inplace=True
)
max_total_return_record_df_R_count.reset_index(drop=True, inplace=True)
# 统计K线条件的记录数目
max_total_return_record_df_K_count = (
max_total_return_record_df.groupby(["K线条件"], dropna=False)
.size()
.reset_index(name="数量")
)
max_total_return_record_df_K_count.sort_values(
by="数量", ascending=False, inplace=True
)
max_total_return_record_df_K_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", "数量"] columns=["方向", "根据SAR", "R算法", "K线条件", "数量"]
) )
max_total_return_record_df_direction_count = pd.DataFrame( max_total_return_record_df_direction_count = pd.DataFrame(
columns=["方向", "数量"] 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_K_count = pd.DataFrame(columns=["K线条件", "数量"])
result = { result = {
"statistics_summary_df": statistics_summary_df, "statistics_summary_df": statistics_summary_df,
@ -231,6 +269,8 @@ def statistics_summary(trades_summary_df: pd.DataFrame):
"max_total_return_record_df_grouped_count": max_total_return_record_df_grouped_count, "max_total_return_record_df_grouped_count": max_total_return_record_df_grouped_count,
"max_total_return_record_df_direction_count": max_total_return_record_df_direction_count, "max_total_return_record_df_direction_count": max_total_return_record_df_direction_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_sar_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,
} }
return result return result
@ -335,9 +375,5 @@ def test():
if __name__ == "__main__": if __name__ == "__main__":
# main() main()
chart_path = r"./output/trade_sandbox/orb_strategy/chart/"
excel_file_path = r"./output/trade_sandbox/orb_strategy/excel/summary/orb_strategy_summary_20250902174203.xlsx"
copy_chart_to_excel(chart_path, excel_file_path)
# test() # test()