增加交易策略、交易指标、量化库代码等文件夹
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,297 @@
|
||||
import backtrader as bt
|
||||
from datetime import datetime
|
||||
from datetime import time as s_time
|
||||
from backtrader.feeds import GenericCSVData
|
||||
import numpy as np
|
||||
|
||||
# import pandas as pd
|
||||
import itertools
|
||||
import multiprocessing
|
||||
import talib as tb
|
||||
|
||||
|
||||
class GenericCSV_SIG(GenericCSVData):
|
||||
lines = ("sig", "delta")
|
||||
params = (("sig", 6), ("delta", 8))
|
||||
|
||||
|
||||
class MyStrategy_固定止损_跟踪止盈(bt.Strategy):
|
||||
params = (
|
||||
("trailing_stop_percent", 0.02),
|
||||
("fixed_stop_loss_percent", 0.01),
|
||||
("duiji", 1),
|
||||
("cout_delta", 1),
|
||||
("delta", 1),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.Lots = 1
|
||||
self.signal = self.datas[0].sig
|
||||
self.delta = self.datas[0].delta
|
||||
self.pos = 0
|
||||
self.high = self.datas[0].high
|
||||
self.low = self.datas[0].low
|
||||
self.closes = self.datas[0].close
|
||||
self.open = self.datas[0].open
|
||||
self.trailing_stop_percent = self.params.trailing_stop_percent
|
||||
self.short_trailing_stop_price = 0
|
||||
self.long_trailing_stop_price = 0
|
||||
self.fixed_stop_loss_percent = self.params.fixed_stop_loss_percent
|
||||
self.sl_long_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_long = 0
|
||||
self.out_short = 0
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.rinei_mean = 0
|
||||
self.reniei_bop = []
|
||||
self.datetime_list = []
|
||||
self.high_list = []
|
||||
self.low_list = []
|
||||
self.close_list = []
|
||||
self.opens_list = []
|
||||
self.deltas_list = []
|
||||
self.delta_cumsum = []
|
||||
self.barN = 0
|
||||
|
||||
def log(self, txt, dt=None):
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print("%s, %s" % (dt.isoformat(), txt))
|
||||
|
||||
def next(self):
|
||||
self.barN += 1
|
||||
# position = self.getposition(self.datas[0]).size
|
||||
dt = bt.num2date(self.data.datetime[0])
|
||||
|
||||
def 每日重置数据():
|
||||
current_time = dt.time()
|
||||
clearing_time1_start = s_time(14, 55)
|
||||
clearing_time1_end = s_time(15, 0)
|
||||
clearing_time2_start = s_time(2, 25)
|
||||
clearing_time2_end = s_time(2, 30)
|
||||
clearing_executed = False
|
||||
if clearing_time1_start <= current_time <= clearing_time1_end and not clearing_executed:
|
||||
clearing_executed = True
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
elif clearing_time2_start <= current_time <= clearing_time2_end and not clearing_executed:
|
||||
clearing_executed = True
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
else:
|
||||
self.rinei_ma.append(self.closes[0])
|
||||
self.renei_open_ma.append(self.open[0])
|
||||
self.renei_high_ma.append(self.high[0])
|
||||
self.renei_low_ma.append(self.low[0])
|
||||
self.rinei_mean = np.mean(self.rinei_ma)
|
||||
self.reniei_bop = tb.BOP(
|
||||
np.array(self.renei_open_ma),
|
||||
np.array(self.renei_high_ma),
|
||||
np.array(self.renei_low_ma),
|
||||
np.array(self.rinei_ma),
|
||||
)
|
||||
clearing_executed = False
|
||||
return clearing_executed
|
||||
|
||||
run_kg = 每日重置数据()
|
||||
|
||||
if self.data.volume[0] <= 0:
|
||||
return
|
||||
|
||||
if self.long_trailing_stop_price > 0 and self.pos > 0:
|
||||
self.long_trailing_stop_price = max(self.low[0], self.long_trailing_stop_price)
|
||||
if self.short_trailing_stop_price > 0 and self.pos < 0:
|
||||
self.short_trailing_stop_price = min(self.high[0], self.short_trailing_stop_price)
|
||||
|
||||
self.out_long = self.long_trailing_stop_price * (1 - self.trailing_stop_percent)
|
||||
self.out_short = self.short_trailing_stop_price * (1 + self.trailing_stop_percent)
|
||||
|
||||
if (
|
||||
self.out_long > 0
|
||||
and self.low[0] < self.out_long
|
||||
and self.pos > 0
|
||||
and self.sl_long_price > 0
|
||||
and self.low[0] > self.sl_long_price
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
if (
|
||||
self.out_short > 0
|
||||
and self.high[0] > self.out_short
|
||||
and self.pos < 0
|
||||
and self.sl_shor_price > 0
|
||||
and self.high[0] < self.sl_shor_price
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
self.fixed_stop_loss_L = self.sl_long_price * (1 - self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_long_price > 0
|
||||
and self.fixed_stop_loss_L > 0
|
||||
and self.pos > 0
|
||||
and self.closes[0] < self.fixed_stop_loss_L
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
self.fixed_stop_loss_S = self.sl_shor_price * (1 + self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_shor_price > 0
|
||||
and self.fixed_stop_loss_S > 0
|
||||
and self.pos < 0
|
||||
and self.closes[0] > self.fixed_stop_loss_S
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
self.datetime_list.append(dt)
|
||||
self.high_list.append(self.data.high[0])
|
||||
self.low_list.append(self.data.low[0])
|
||||
self.close_list.append(self.data.close[0])
|
||||
self.opens_list.append(self.data.open[0])
|
||||
self.deltas_list.append(self.data.delta[0])
|
||||
self.delta_cumsum.append(sum(self.deltas_list))
|
||||
|
||||
if run_kg is False:
|
||||
t3 = tb.T3(np.array(self.deltas_list))
|
||||
t3_cum = tb.T3(np.array(self.delta_cumsum))
|
||||
# 开多组合 = self.rinei_mean > 0 and self.closes[0] > self.rinei_mean and self.signal[0] > self.params.duiji and self.data.delta[0] > self.params.delta and self.delta_cumsum[-1] > self.params.cout_delta
|
||||
# 开空组合 = self.rinei_mean > 0 and self.closes[0] < self.rinei_mean and self.signal[0] < -self.params.duiji and self.data.delta[0] < -self.params.delta and self.delta_cumsum[-1] < -self.params.cout_delta
|
||||
开多组合 = (
|
||||
self.reniei_bop[-1] > 0
|
||||
and self.signal[0] > self.params.duiji
|
||||
and self.data.delta[0] > t3[-1] # self.params.delta
|
||||
and self.delta_cumsum[-1] > t3_cum[-1] # self.params.cout_delta
|
||||
)
|
||||
开空组合 = (
|
||||
self.reniei_bop[-1] < 0
|
||||
and self.signal[0] < -self.params.duiji
|
||||
and self.data.delta[0] < t3[-1] # -self.params.delta
|
||||
and self.delta_cumsum[-1] < t3_cum[-1] # -self.params.cout_delta
|
||||
)
|
||||
平多条件 = self.pos < 0 and self.signal[0] > self.params.duiji
|
||||
平空条件 = self.pos > 0 and self.signal[0] < -self.params.duiji
|
||||
|
||||
if self.pos != 1:
|
||||
if 平多条件:
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
if 开多组合:
|
||||
self.buy(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = 1
|
||||
self.long_trailing_stop_price = self.low[0]
|
||||
self.sl_long_price = self.data.open[0]
|
||||
|
||||
if self.pos != -1:
|
||||
if 平空条件:
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
if 开空组合:
|
||||
self.sell(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = -1
|
||||
self.short_trailing_stop_price = self.high[0]
|
||||
self.sl_shor_price = self.data.open[0]
|
||||
|
||||
|
||||
def evaluate_strategy(trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta, csv_file):
|
||||
cerebro = bt.Cerebro()
|
||||
cerebro.addstrategy(
|
||||
MyStrategy_固定止损_跟踪止盈,
|
||||
trailing_stop_percent=trailing_stop_percent,
|
||||
fixed_stop_loss_percent=fixed_stop_loss_percent,
|
||||
duiji=duiji,
|
||||
cout_delta=cout_delta,
|
||||
delta=delta,
|
||||
)
|
||||
data = GenericCSV_SIG(
|
||||
dataname=csv_file,
|
||||
fromdate=datetime(2020, 1, 1),
|
||||
todate=datetime(2023, 12, 29),
|
||||
timeframe=bt.TimeFrame.Minutes,
|
||||
nullvalue=0.0,
|
||||
dtformat="%Y-%m-%d %H:%M:%S",
|
||||
datetime=0,
|
||||
high=3,
|
||||
low=4,
|
||||
open=2,
|
||||
close=1,
|
||||
volume=5,
|
||||
openinterest=None,
|
||||
sig=6,
|
||||
delta=8,
|
||||
)
|
||||
cerebro.adddata(data)
|
||||
cerebro.broker.setcash(300000.0)
|
||||
cerebro.broker.setcommission(commission=14, margin=150000.0, mult=300)
|
||||
cerebro.run()
|
||||
return cerebro.broker.getvalue(), (trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta)
|
||||
|
||||
|
||||
def run_backtest(params):
|
||||
trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta, csv_file = params
|
||||
return evaluate_strategy(trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta, csv_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
csv_file = r"D:\BaiduNetdiskDownload\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IM888\IM888_rs_2023_5T_back_ofdata_dj_new.csv"
|
||||
|
||||
trailing_stop_percents = np.arange(0.005, 0.025, 0.005)
|
||||
fixed_stop_loss_percents = np.arange(0.01, 0.050, 0.01)
|
||||
duiji = np.arange(1, 4, 1)
|
||||
cout_delta = np.arange(100000, 200000, 100000) # (500, 3500, 500)
|
||||
delta = np.arange(100000, 200000, 100000) # (500, 3500, 500)
|
||||
|
||||
combinations = list(itertools.product(trailing_stop_percents, fixed_stop_loss_percents, duiji, cout_delta, delta))
|
||||
combinations = [(tsp, fslp, d, cd, dl, csv_file) for tsp, fslp, d, cd, dl in combinations]
|
||||
|
||||
with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
|
||||
results = pool.map(run_backtest, combinations)
|
||||
|
||||
best_value = 0
|
||||
best_parameters = None
|
||||
|
||||
for value, params in results:
|
||||
if value > best_value:
|
||||
best_value = value
|
||||
best_parameters = params
|
||||
print(f"combo: {params}, value: {value}, best_value: {best_value}, best_parameters: {best_parameters}")
|
||||
|
||||
print(f"最佳参数组合: 跟踪止损百分比 {best_parameters[0]}%, 固定止损百分比 {best_parameters[1]}%")
|
||||
print(f"最大市值: {best_value}")
|
||||
|
||||
# trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta
|
||||
# IM
|
||||
# 5M:(0.01, 0.02, 2, 700000, 500000)
|
||||
# 1M:(0.005, 0.02, 3, 100000, 200000)
|
||||
# IF
|
||||
# 5M:(0.005, 0.02, 1, 100000, 400000)
|
||||
@@ -0,0 +1,286 @@
|
||||
import backtrader as bt
|
||||
from datetime import datetime
|
||||
from datetime import time as s_time
|
||||
from backtrader.feeds import GenericCSVData
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import itertools
|
||||
import multiprocessing
|
||||
|
||||
import talib as tb
|
||||
|
||||
|
||||
class GenericCSV_SIG(GenericCSVData):
|
||||
lines = ("sig", "delta")
|
||||
params = (("sig", 6), ("delta", 8))
|
||||
|
||||
|
||||
class MyStrategy_固定止损_跟踪止盈(bt.Strategy):
|
||||
params = (
|
||||
("trailing_stop_percent", 0.02),
|
||||
("fixed_stop_loss_percent", 0.01),
|
||||
("duiji", 1),
|
||||
("cout_delta", 1),
|
||||
("delta", 1),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.Lots = 1
|
||||
self.signal = self.datas[0].sig
|
||||
self.delta = self.datas[0].delta
|
||||
self.pos = 0
|
||||
self.high = self.datas[0].high
|
||||
self.low = self.datas[0].low
|
||||
self.closes = self.datas[0].close
|
||||
self.open = self.datas[0].open
|
||||
self.trailing_stop_percent = self.params.trailing_stop_percent
|
||||
self.short_trailing_stop_price = 0
|
||||
self.long_trailing_stop_price = 0
|
||||
self.fixed_stop_loss_percent = self.params.fixed_stop_loss_percent
|
||||
self.sl_long_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_long = 0
|
||||
self.out_short = 0
|
||||
self.rinei_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.rinei_mean = 0
|
||||
self.reniei_sar = []
|
||||
self.datetime_list = []
|
||||
self.high_list = []
|
||||
self.low_list = []
|
||||
self.close_list = []
|
||||
self.opens_list = []
|
||||
self.deltas_list = []
|
||||
self.delta_cumsum = []
|
||||
self.barN = 0
|
||||
|
||||
def log(self, txt, dt=None):
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print("%s, %s" % (dt.isoformat(), txt))
|
||||
|
||||
def next(self):
|
||||
self.barN += 1
|
||||
position = self.getposition(self.datas[0]).size
|
||||
dt = bt.num2date(self.data.datetime[0])
|
||||
|
||||
def 每日重置数据():
|
||||
current_time = dt.time()
|
||||
clearing_time1_start = s_time(14, 55)
|
||||
clearing_time1_end = s_time(15, 0)
|
||||
clearing_time2_start = s_time(2, 25)
|
||||
clearing_time2_end = s_time(2, 30)
|
||||
clearing_executed = False
|
||||
if clearing_time1_start <= current_time <= clearing_time1_end and not clearing_executed:
|
||||
clearing_executed = True
|
||||
self.rinei_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
elif clearing_time2_start <= current_time <= clearing_time2_end and not clearing_executed:
|
||||
clearing_executed = True
|
||||
self.rinei_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
else:
|
||||
self.rinei_ma.append(self.closes[0])
|
||||
self.renei_high_ma.append(self.high[0])
|
||||
self.renei_low_ma.append(self.low[0])
|
||||
self.rinei_mean = np.mean(self.rinei_ma)
|
||||
self.reniei_sar = tb.SAR(np.array(self.renei_high_ma), np.array(self.renei_low_ma), 0.02, 0.2)
|
||||
clearing_executed = False
|
||||
return clearing_executed
|
||||
|
||||
run_kg = 每日重置数据()
|
||||
|
||||
if self.data.volume[0] <= 0:
|
||||
return
|
||||
|
||||
if self.long_trailing_stop_price > 0 and self.pos > 0:
|
||||
self.long_trailing_stop_price = max(self.low[0], self.long_trailing_stop_price)
|
||||
if self.short_trailing_stop_price > 0 and self.pos < 0:
|
||||
self.short_trailing_stop_price = min(self.high[0], self.short_trailing_stop_price)
|
||||
|
||||
self.out_long = self.long_trailing_stop_price * (1 - self.trailing_stop_percent)
|
||||
self.out_short = self.short_trailing_stop_price * (1 + self.trailing_stop_percent)
|
||||
|
||||
if (
|
||||
self.out_long > 0
|
||||
and self.low[0] < self.out_long
|
||||
and self.pos > 0
|
||||
and self.sl_long_price > 0
|
||||
and self.low[0] > self.sl_long_price
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
if (
|
||||
self.out_short > 0
|
||||
and self.high[0] > self.out_short
|
||||
and self.pos < 0
|
||||
and self.sl_shor_price > 0
|
||||
and self.high[0] < self.sl_shor_price
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
self.fixed_stop_loss_L = self.sl_long_price * (1 - self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_long_price > 0
|
||||
and self.fixed_stop_loss_L > 0
|
||||
and self.pos > 0
|
||||
and self.closes[0] < self.fixed_stop_loss_L
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
self.fixed_stop_loss_S = self.sl_shor_price * (1 + self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_shor_price > 0
|
||||
and self.fixed_stop_loss_S > 0
|
||||
and self.pos < 0
|
||||
and self.closes[0] > self.fixed_stop_loss_S
|
||||
):
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
self.datetime_list.append(dt)
|
||||
self.high_list.append(self.data.high[0])
|
||||
self.low_list.append(self.data.low[0])
|
||||
self.close_list.append(self.data.close[0])
|
||||
self.opens_list.append(self.data.open[0])
|
||||
self.deltas_list.append(self.data.delta[0])
|
||||
self.delta_cumsum.append(sum(self.deltas_list))
|
||||
|
||||
if run_kg == False:
|
||||
# 开多组合 = self.rinei_mean > 0 and self.closes[0] > self.rinei_mean and self.signal[0] > self.params.duiji and self.data.delta[0] > self.params.delta and self.delta_cumsum[-1] > self.params.cout_delta
|
||||
# 开空组合 = self.rinei_mean > 0 and self.closes[0] < self.rinei_mean and self.signal[0] < -self.params.duiji and self.data.delta[0] < -self.params.delta and self.delta_cumsum[-1] < -self.params.cout_delta
|
||||
开多组合 = (
|
||||
self.reniei_sar[-1] > self.closes[0]
|
||||
and self.signal[0] > self.params.duiji
|
||||
and self.data.delta[0] > self.params.delta
|
||||
and self.delta_cumsum[-1] > self.params.cout_delta
|
||||
)
|
||||
开空组合 = (
|
||||
self.reniei_sar[-1] < self.closes[0]
|
||||
and self.signal[0] < -self.params.duiji
|
||||
and self.data.delta[0] < -self.params.delta
|
||||
and self.delta_cumsum[-1] < -self.params.cout_delta
|
||||
)
|
||||
平多条件 = self.pos < 0 and self.signal[0] > self.params.duiji
|
||||
平空条件 = self.pos > 0 and self.signal[0] < -self.params.duiji
|
||||
|
||||
if self.pos != 1:
|
||||
if 平多条件:
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
if 开多组合:
|
||||
self.buy(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = 1
|
||||
self.long_trailing_stop_price = self.low[0]
|
||||
self.sl_long_price = self.data.open[0]
|
||||
|
||||
if self.pos != -1:
|
||||
if 平空条件:
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
if 开空组合:
|
||||
self.sell(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = -1
|
||||
self.short_trailing_stop_price = self.high[0]
|
||||
self.sl_shor_price = self.data.open[0]
|
||||
|
||||
|
||||
def evaluate_strategy(trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta, csv_file):
|
||||
cerebro = bt.Cerebro()
|
||||
cerebro.addstrategy(
|
||||
MyStrategy_固定止损_跟踪止盈,
|
||||
trailing_stop_percent=trailing_stop_percent,
|
||||
fixed_stop_loss_percent=fixed_stop_loss_percent,
|
||||
duiji=duiji,
|
||||
cout_delta=cout_delta,
|
||||
delta=delta,
|
||||
)
|
||||
data = GenericCSV_SIG(
|
||||
dataname=csv_file,
|
||||
fromdate=datetime(2020, 1, 1),
|
||||
todate=datetime(2023, 12, 29),
|
||||
timeframe=bt.TimeFrame.Minutes,
|
||||
nullvalue=0.0,
|
||||
dtformat="%Y-%m-%d %H:%M:%S",
|
||||
datetime=0,
|
||||
high=3,
|
||||
low=4,
|
||||
open=2,
|
||||
close=1,
|
||||
volume=5,
|
||||
openinterest=None,
|
||||
sig=6,
|
||||
delta=8,
|
||||
)
|
||||
cerebro.adddata(data)
|
||||
cerebro.broker.setcash(300000.0)
|
||||
cerebro.broker.setcommission(commission=14, margin=150000.0, mult=300)
|
||||
cerebro.run()
|
||||
return cerebro.broker.getvalue(), (trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta)
|
||||
|
||||
|
||||
def run_backtest(params):
|
||||
trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta, csv_file = params
|
||||
return evaluate_strategy(trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta, csv_file)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
csv_file = r"D:\BaiduNetdiskDownload\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IM888\IM888_rs_2023_5T_back_ofdata_dj.csv"
|
||||
|
||||
trailing_stop_percents = np.arange(0.005, 0.025, 0.005)
|
||||
fixed_stop_loss_percents = np.arange(0.01, 0.050, 0.01)
|
||||
duiji = np.arange(1, 4, 1)
|
||||
cout_delta = np.arange(100000, 800000, 100000) # (500, 3500, 500)
|
||||
delta = np.arange(100000, 800000, 100000) # (500, 3500, 500)
|
||||
|
||||
combinations = list(itertools.product(trailing_stop_percents, fixed_stop_loss_percents, duiji, cout_delta, delta))
|
||||
combinations = [(tsp, fslp, d, cd, dl, csv_file) for tsp, fslp, d, cd, dl in combinations]
|
||||
|
||||
with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
|
||||
results = pool.map(run_backtest, combinations)
|
||||
|
||||
best_value = 0
|
||||
best_parameters = None
|
||||
|
||||
for value, params in results:
|
||||
if value > best_value:
|
||||
best_value = value
|
||||
best_parameters = params
|
||||
print(f"combo: {params}, value: {value}, best_value: {best_value}, best_parameters: {best_parameters}")
|
||||
|
||||
print(f"最佳参数组合: 跟踪止损百分比 {best_parameters[0]}%, 固定止损百分比 {best_parameters[1]}%")
|
||||
print(f"最大市值: {best_value}")
|
||||
|
||||
# trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta
|
||||
# IM
|
||||
# 5M:(0.01, 0.02, 2, 700000, 500000)
|
||||
# 1M:(0.005, 0.02, 3, 100000, 200000)
|
||||
# IF
|
||||
# 5M:(0.005, 0.02, 1, 100000, 400000)
|
||||
@@ -0,0 +1,382 @@
|
||||
import backtrader as bt
|
||||
from datetime import datetime
|
||||
from datetime import time as s_time
|
||||
from backtrader.feeds import GenericCSVData
|
||||
import numpy as np
|
||||
|
||||
# import pandas as pd
|
||||
import itertools
|
||||
import multiprocessing
|
||||
import talib as tb
|
||||
|
||||
|
||||
class GenericCSV_SIG(GenericCSVData):
|
||||
lines = ("sig", "delta")
|
||||
params = (("sig", 6), ("delta", 8))
|
||||
|
||||
|
||||
class MyStrategy_固定止损_跟踪止盈(bt.Strategy):
|
||||
params = (
|
||||
("trailing_stop_percent", 0.02),
|
||||
("fixed_stop_loss_percent", 0.01),
|
||||
("duiji", 1),
|
||||
("cout_delta", 1),
|
||||
("delta", 1),
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.Lots = 1
|
||||
self.signal = self.datas[0].sig
|
||||
self.delta = self.datas[0].delta
|
||||
self.pos = 0
|
||||
self.high = self.datas[0].high
|
||||
self.low = self.datas[0].low
|
||||
self.closes = self.datas[0].close
|
||||
self.open = self.datas[0].open
|
||||
self.trailing_stop_percent = self.params.trailing_stop_percent
|
||||
self.short_trailing_stop_price = 0
|
||||
self.long_trailing_stop_price = 0
|
||||
self.fixed_stop_loss_percent = self.params.fixed_stop_loss_percent
|
||||
self.sl_long_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_long = 0
|
||||
self.out_short = 0
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.rinei_mean = 0
|
||||
self.reniei_indicator = []
|
||||
self.datetime_list = []
|
||||
self.high_list = []
|
||||
self.low_list = []
|
||||
self.close_list = []
|
||||
self.opens_list = []
|
||||
self.deltas_list = []
|
||||
self.delta_cumsum = []
|
||||
self.barN = 0
|
||||
|
||||
def log(self, txt, dt=None):
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print("%s, %s" % (dt.isoformat(), txt))
|
||||
|
||||
def next(self):
|
||||
self.barN += 1
|
||||
# position = self.getposition(self.datas[0]).size
|
||||
dt = bt.num2date(self.data.datetime[0])
|
||||
|
||||
def 每日重置数据():
|
||||
current_time = dt.time()
|
||||
clearing_time1_start = s_time(14, 55)
|
||||
clearing_time1_end = s_time(15, 0)
|
||||
clearing_time2_start = s_time(2, 25)
|
||||
clearing_time2_end = s_time(2, 30)
|
||||
clearing_executed = False
|
||||
if (
|
||||
clearing_time1_start <= current_time <= clearing_time1_end
|
||||
and not clearing_executed
|
||||
):
|
||||
clearing_executed = True
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
elif (
|
||||
clearing_time2_start <= current_time <= clearing_time2_end
|
||||
and not clearing_executed
|
||||
):
|
||||
clearing_executed = True
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
else:
|
||||
self.rinei_ma.append(self.closes[0])
|
||||
self.renei_open_ma.append(self.open[0])
|
||||
self.renei_high_ma.append(self.high[0])
|
||||
self.renei_low_ma.append(self.low[0])
|
||||
self.rinei_mean = np.mean(self.rinei_ma)
|
||||
self.reniei_indicator = tb.AROONOSC(
|
||||
np.array(self.renei_high_ma),
|
||||
np.array(self.renei_low_ma),
|
||||
self.params.delta,
|
||||
)
|
||||
clearing_executed = False
|
||||
return clearing_executed
|
||||
|
||||
run_kg = 每日重置数据()
|
||||
|
||||
if self.data.volume[0] <= 0:
|
||||
return
|
||||
|
||||
if self.long_trailing_stop_price > 0 and self.pos > 0:
|
||||
self.long_trailing_stop_price = max(
|
||||
self.low[0], self.long_trailing_stop_price
|
||||
)
|
||||
if self.short_trailing_stop_price > 0 and self.pos < 0:
|
||||
self.short_trailing_stop_price = min(
|
||||
self.high[0], self.short_trailing_stop_price
|
||||
)
|
||||
|
||||
self.out_long = self.long_trailing_stop_price * (1 - self.trailing_stop_percent)
|
||||
self.out_short = self.short_trailing_stop_price * (
|
||||
1 + self.trailing_stop_percent
|
||||
)
|
||||
|
||||
if (
|
||||
self.out_long > 0
|
||||
and self.low[0] < self.out_long
|
||||
and self.pos > 0
|
||||
and self.sl_long_price > 0
|
||||
and self.low[0] > self.sl_long_price
|
||||
):
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
if (
|
||||
self.out_short > 0
|
||||
and self.high[0] > self.out_short
|
||||
and self.pos < 0
|
||||
and self.sl_shor_price > 0
|
||||
and self.high[0] < self.sl_shor_price
|
||||
):
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
self.fixed_stop_loss_L = self.sl_long_price * (1 - self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_long_price > 0
|
||||
and self.fixed_stop_loss_L > 0
|
||||
and self.pos > 0
|
||||
and self.closes[0] < self.fixed_stop_loss_L
|
||||
):
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
self.fixed_stop_loss_S = self.sl_shor_price * (1 + self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_shor_price > 0
|
||||
and self.fixed_stop_loss_S > 0
|
||||
and self.pos < 0
|
||||
and self.closes[0] > self.fixed_stop_loss_S
|
||||
):
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
self.datetime_list.append(dt)
|
||||
self.high_list.append(self.data.high[0])
|
||||
self.low_list.append(self.data.low[0])
|
||||
self.close_list.append(self.data.close[0])
|
||||
self.opens_list.append(self.data.open[0])
|
||||
self.deltas_list.append(self.data.delta[0])
|
||||
self.delta_cumsum.append(sum(self.deltas_list))
|
||||
|
||||
if run_kg is False:
|
||||
# t3 = tb.T3(np.array(self.deltas_list))
|
||||
# t3_cum = tb.T3(np.array(self.delta_cumsum))
|
||||
# 开多组合 = self.rinei_mean > 0 and self.closes[0] > self.rinei_mean and self.signal[0] > self.params.duiji and self.data.delta[0] > self.params.delta and self.delta_cumsum[-1] > self.params.cout_delta
|
||||
# 开空组合 = self.rinei_mean > 0 and self.closes[0] < self.rinei_mean and self.signal[0] < -self.params.duiji and self.data.delta[0] < -self.params.delta and self.delta_cumsum[-1] < -self.params.cout_delta
|
||||
开多组合 = (
|
||||
self.reniei_indicator[-1] > 0
|
||||
and self.signal[0] > self.params.duiji
|
||||
and self.data.delta[0]
|
||||
> max(self.deltas_list[-self.params.cout_delta : -1])
|
||||
and self.delta_cumsum[-1]
|
||||
> max(self.delta_cumsum[-self.params.cout_delta : -1])
|
||||
)
|
||||
开空组合 = (
|
||||
self.reniei_indicator[-1] < 0
|
||||
and self.signal[0] < -self.params.duiji
|
||||
and self.data.delta[0]
|
||||
< min(self.deltas_list[-self.params.cout_delta : -1])
|
||||
and self.delta_cumsum[-1]
|
||||
< min(self.delta_cumsum[-self.params.cout_delta : -1])
|
||||
)
|
||||
平多条件 = self.pos < 0 and self.signal[0] > self.params.duiji
|
||||
平空条件 = self.pos > 0 and self.signal[0] < -self.params.duiji
|
||||
|
||||
if self.pos != 1:
|
||||
if 平多条件:
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
if 开多组合:
|
||||
self.buy(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=1,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.pos = 1
|
||||
self.long_trailing_stop_price = self.low[0]
|
||||
self.sl_long_price = self.data.open[0]
|
||||
|
||||
if self.pos != -1:
|
||||
if 平空条件:
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
if 开空组合:
|
||||
self.sell(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=1,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.pos = -1
|
||||
self.short_trailing_stop_price = self.high[0]
|
||||
self.sl_shor_price = self.data.open[0]
|
||||
|
||||
|
||||
def evaluate_strategy(
|
||||
trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta, csv_file
|
||||
):
|
||||
cerebro = bt.Cerebro()
|
||||
cerebro.addstrategy(
|
||||
MyStrategy_固定止损_跟踪止盈,
|
||||
trailing_stop_percent=trailing_stop_percent,
|
||||
fixed_stop_loss_percent=fixed_stop_loss_percent,
|
||||
duiji=duiji,
|
||||
cout_delta=cout_delta,
|
||||
delta=delta,
|
||||
)
|
||||
data = GenericCSV_SIG(
|
||||
dataname=csv_file,
|
||||
fromdate=datetime(2020, 1, 1),
|
||||
todate=datetime(2023, 12, 29),
|
||||
timeframe=bt.TimeFrame.Minutes,
|
||||
nullvalue=0.0,
|
||||
dtformat="%Y-%m-%d %H:%M:%S",
|
||||
datetime=0,
|
||||
high=3,
|
||||
low=4,
|
||||
open=2,
|
||||
close=1,
|
||||
volume=5,
|
||||
openinterest=None,
|
||||
sig=6,
|
||||
delta=8,
|
||||
)
|
||||
cerebro.adddata(data)
|
||||
cerebro.broker.setcash(300000.0)
|
||||
cerebro.broker.setcommission(commission=14, margin=150000.0, mult=300)
|
||||
cerebro.run()
|
||||
return cerebro.broker.getvalue(), (
|
||||
trailing_stop_percent,
|
||||
fixed_stop_loss_percent,
|
||||
duiji,
|
||||
cout_delta,
|
||||
delta,
|
||||
)
|
||||
|
||||
|
||||
def run_backtest(params):
|
||||
(
|
||||
trailing_stop_percent,
|
||||
fixed_stop_loss_percent,
|
||||
duiji,
|
||||
cout_delta,
|
||||
delta,
|
||||
csv_file,
|
||||
) = params
|
||||
return evaluate_strategy(
|
||||
trailing_stop_percent,
|
||||
fixed_stop_loss_percent,
|
||||
duiji,
|
||||
cout_delta,
|
||||
delta,
|
||||
csv_file,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
csv_file = r"E:\of_data\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IM888\IM888_rs_2023_5T_back_ofdata_dj.csv"
|
||||
|
||||
trailing_stop_percents = np.arange(0.005, 0.025, 0.005)
|
||||
fixed_stop_loss_percents = np.arange(0.01, 0.050, 0.01)
|
||||
duiji = np.arange(0, 4, 1)
|
||||
cout_delta = np.arange(5, 20, 5) # (500, 3500, 500)
|
||||
delta = np.arange(20, 120, 10) # (500, 3500, 500)
|
||||
|
||||
combinations = list(
|
||||
itertools.product(
|
||||
trailing_stop_percents, fixed_stop_loss_percents, duiji, cout_delta, delta
|
||||
)
|
||||
)
|
||||
combinations = [
|
||||
(tsp, fslp, d, cd, dl, csv_file) for tsp, fslp, d, cd, dl in combinations
|
||||
]
|
||||
|
||||
with multiprocessing.Pool(processes=multiprocessing.cpu_count()) as pool:
|
||||
results = pool.map(run_backtest, combinations)
|
||||
|
||||
best_value = 0
|
||||
best_parameters = None
|
||||
|
||||
for value, params in results:
|
||||
if value > best_value:
|
||||
best_value = value
|
||||
best_parameters = params
|
||||
print(
|
||||
f"combo: {params}, value: {value}, best_value: {best_value}, best_parameters: {best_parameters}"
|
||||
)
|
||||
|
||||
print(
|
||||
f"最佳参数组合: 跟踪止损百分比 {best_parameters[0]}%, 固定止损百分比 {best_parameters[1]}%"
|
||||
)
|
||||
print(f"最大市值: {best_value}")
|
||||
|
||||
# trailing_stop_percent, fixed_stop_loss_percent, duiji, cout_delta, delta
|
||||
# IM
|
||||
# 5M:(0.01, 0.02, 2, 700000, 500000)
|
||||
# 1M:(0.005, 0.02, 3, 100000, 200000)
|
||||
# IF
|
||||
# 5M:(0.005, 0.02, 1, 100000, 400000)
|
||||
@@ -0,0 +1,553 @@
|
||||
"""
|
||||
以下是代码的详细说明:
|
||||
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
1.
|
||||
导入必要的模块和库:
|
||||
backtrader 用于回测功能
|
||||
datetime 用于处理日期和时间
|
||||
GenericCSVData 用于从CSV文件加载数据
|
||||
numpy 用于数值操作
|
||||
time 用于时间相关操作
|
||||
matplotlib.pyplot 用于绘图
|
||||
|
||||
2. 定义自定义手续费模板MyCommission
|
||||
继承自bt.CommInfoBase
|
||||
|
||||
3.
|
||||
定义自定义数据源类 GenericCSV_SIG:
|
||||
继承自 GenericCSVData,并添加了两个额外的行:'sig'和'delta'
|
||||
定义了参数 'sig'和'delta'
|
||||
|
||||
4.
|
||||
定义 MyStrategy_固定止损_跟踪止盈 类:
|
||||
继承自 bt.Strategy(backtrader的基础策略类)
|
||||
定义了两个参数:trailing_stop_percent 和 fixed_stop_loss_percent
|
||||
初始化策略并设置各种变量和指标
|
||||
实现了 next 方法,该方法在数据源的每个新的K线出现时被调用
|
||||
根据当前K线数据更新跟踪止盈价格
|
||||
实现了跟踪止盈出场和固定止损出场
|
||||
根据信号处理多头和空头仓位
|
||||
在策略执行过程中打印调试信息
|
||||
|
||||
5.
|
||||
if __name__ == "__main__": 代码块:
|
||||
使用 Cerebro 实例设置回测环境
|
||||
使用 GenericCSV_SIG 数据源从CSV文件加载数据
|
||||
将数据源和策略添加到 Cerebro 实例中
|
||||
添加观察者和分析器以评估性能
|
||||
设置初始资金和经纪人参数
|
||||
运行回测并获取结果
|
||||
打印回测报告,包括收益率、回撤、胜率和交易统计数据
|
||||
|
||||
6.使用前事项:
|
||||
1、主程序中修改ofdata_dj文件地址、png_filepath地址
|
||||
2、修改clearing_time2_start、clearing_time2_stop
|
||||
3、修改交易参数:lots、跟踪止损百分、固定止损百分比、duiji、cout_delta、delta
|
||||
4、修改资金参数:初始资金;回测参数:回测时间段、佣金、单边保证金、手续费;
|
||||
"""
|
||||
|
||||
import backtrader as bt
|
||||
from datetime import datetime
|
||||
from datetime import time as s_time
|
||||
from backtrader.feeds import GenericCSVData
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
import os
|
||||
|
||||
import talib as tb # jerom注释: 增加talib库
|
||||
|
||||
# import time
|
||||
# import matplotlib.pyplot as plt
|
||||
|
||||
手续费汇总 = 0
|
||||
|
||||
|
||||
class GenericCSV_SIG(GenericCSVData):
|
||||
# 从基类继承,添加一个 'sig',‘delta’行
|
||||
lines = ("sig", "delta")
|
||||
# 添加参数为从基类继承的参数
|
||||
params = (("sig", 6), ("delta", 8))
|
||||
|
||||
|
||||
class MyStrategy_固定止损_跟踪止盈(bt.Strategy):
|
||||
params = (
|
||||
("trailing_stop_percent", 0.005), # 跟踪止盈百分比
|
||||
("fixed_stop_loss_percent", 0.02), # 固定止损百分比
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.Lots = 1 # 下单手数
|
||||
|
||||
self.signal = self.datas[0].sig # 使用sig字段作为策略的信号字段
|
||||
self.delta = self.datas[0].delta
|
||||
# 获取数据序列别名列表
|
||||
line_aliases = self.datas[0].getlinealiases()
|
||||
self.pos = 0
|
||||
print(line_aliases)
|
||||
self.high = self.datas[0].high
|
||||
self.low = self.datas[0].low
|
||||
self.closes = self.datas[0].close
|
||||
self.open = self.datas[0].open
|
||||
self.trailing_stop_percent = self.params.trailing_stop_percent
|
||||
self.short_trailing_stop_price = 0
|
||||
self.long_trailing_stop_price = 0
|
||||
self.fixed_stop_loss_percent = self.params.fixed_stop_loss_percent
|
||||
self.sl_long_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_long = 0
|
||||
# 240884432
|
||||
self.out_short = 0
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.rinei_mean = 0
|
||||
self.reniei_bop = []
|
||||
|
||||
self.datetime_list = []
|
||||
self.high_list = []
|
||||
self.low_list = []
|
||||
self.close_list = []
|
||||
self.opens_list = []
|
||||
self.deltas_list = []
|
||||
self.delta_cumsum = []
|
||||
|
||||
self.barN = 0
|
||||
self.df = pd.DataFrame(columns=["datetime", "high", "low", "close", "open", "delta", "delta_cumsum"])
|
||||
|
||||
self.trader_df = pd.DataFrame(columns=["open", "high", "low", "close", "volume", "openInterest", "delta"])
|
||||
|
||||
def log(self, txt, dt=None):
|
||||
"""可选,构建策略打印日志的函数:可用于打印订单记录或交易记录等"""
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print("%s, %s" % (dt.isoformat(), txt))
|
||||
|
||||
def notify_order(self, order):
|
||||
# 未被处理的订单
|
||||
if order.status in [order.Submitted, order.Accepted]:
|
||||
return
|
||||
# 已经处理的订单
|
||||
if order.status in [order.Completed, order.Canceled, order.Margin]:
|
||||
global 手续费汇总
|
||||
if order.isbuy():
|
||||
手续费汇总 += order.executed.comm
|
||||
self.log(
|
||||
"BUY EXECUTED, 订单编号:%.0f,成交价格: %.2f, 手续费滑点:%.2f, 成交量: %.2f, 品种: %s,手续费汇总:%.2f"
|
||||
% (
|
||||
order.ref, # 订单编号
|
||||
order.executed.price, # 成交价
|
||||
order.executed.comm, # 佣金
|
||||
order.executed.size, # 成交量
|
||||
order.data._name, # 品种名称
|
||||
手续费汇总,
|
||||
)
|
||||
)
|
||||
|
||||
else: # Sell
|
||||
手续费汇总 += order.executed.comm
|
||||
self.log(
|
||||
"SELL EXECUTED, 订单编号:%.0f,成交价格: %.2f, 手续费滑点:%.2f, 成交量: %.2f, 品种: %s,手续费汇总:%.2f"
|
||||
% (
|
||||
order.ref,
|
||||
order.executed.price,
|
||||
order.executed.comm,
|
||||
order.executed.size,
|
||||
order.data._name,
|
||||
手续费汇总,
|
||||
)
|
||||
)
|
||||
|
||||
def next(self):
|
||||
# 公众号:松鼠Quant
|
||||
# 主页:www.quant789.com
|
||||
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
# 版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
|
||||
# bar线计数初始化
|
||||
self.barN += 1
|
||||
position = self.getposition(self.datas[0]).size
|
||||
# 时间轴
|
||||
dt = bt.num2date(self.data.datetime[0])
|
||||
|
||||
# 更新跟踪止损价格
|
||||
def 每日重置数据():
|
||||
# 获取当前时间
|
||||
current_time = dt.time()
|
||||
# print(current_time)
|
||||
# 设置清仓操作的时间范围1:14:55到15:00
|
||||
clearing_time1_start = s_time(14, 55)
|
||||
clearing_time1_end = s_time(15, 0)
|
||||
|
||||
# 设置清仓操作的时间范围2:00:55到01:00
|
||||
clearing_time2_start = s_time(2, 25)
|
||||
clearing_time2_end = s_time(2, 30)
|
||||
|
||||
# 创建一个标志变量
|
||||
clearing_executed = False
|
||||
|
||||
if clearing_time1_start <= current_time <= clearing_time1_end and not clearing_executed:
|
||||
clearing_executed = True # 设置标志变量为已执行
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
elif clearing_time2_start <= current_time <= clearing_time2_end and not clearing_executed:
|
||||
clearing_executed = True # 设置标志变量为已执行
|
||||
self.rinei_ma = []
|
||||
self.renei_open_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
# 如果不在任何时间范围内,可以执行其他操作
|
||||
else:
|
||||
self.renei_open_ma.append(self.open[0])
|
||||
self.rinei_ma.append(self.closes[0])
|
||||
self.renei_high_ma.append(self.high[0])
|
||||
self.renei_low_ma.append(self.low[0])
|
||||
self.rinei_mean = np.mean(self.rinei_ma)
|
||||
self.reniei_bop = tb.BOP(
|
||||
np.array(self.renei_open_ma),
|
||||
np.array(self.renei_high_ma),
|
||||
np.array(self.renei_low_ma),
|
||||
np.array(self.rinei_ma),
|
||||
)
|
||||
# self.delta_cumsum=[]
|
||||
# self.deltas_list=[]
|
||||
# print('rinei_ma',self.rinei_ma)
|
||||
clearing_executed = False
|
||||
pass
|
||||
|
||||
return clearing_executed
|
||||
|
||||
run_kg = 每日重置数据()
|
||||
|
||||
# 过滤成交量为0或小于0
|
||||
if self.data.volume[0] <= 0:
|
||||
return
|
||||
# print(f'volume,{self.data.volume[0]}')
|
||||
if self.long_trailing_stop_price > 0 and self.pos > 0:
|
||||
# print('datetime+sig: ',dt,'旧多头出线',self.long_trailing_stop_price,'low',self.low[0])
|
||||
|
||||
self.long_trailing_stop_price = (
|
||||
self.low[0] if self.long_trailing_stop_price < self.low[0] else self.long_trailing_stop_price
|
||||
)
|
||||
|
||||
# print('datetime+sig: ',dt,'多头出线',self.long_trailing_stop_price)
|
||||
if self.short_trailing_stop_price > 0 and self.pos < 0:
|
||||
# print('datetime+sig: ',dt,'旧空头出线',self.short_trailing_stop_price,'high',self.high[0])
|
||||
|
||||
self.short_trailing_stop_price = (
|
||||
self.high[0] if self.high[0] < self.short_trailing_stop_price else self.short_trailing_stop_price
|
||||
)
|
||||
|
||||
# print('datetime+sig: ',dt,'空头出线',self.short_trailing_stop_price)
|
||||
|
||||
self.out_long = self.long_trailing_stop_price * (1 - self.trailing_stop_percent)
|
||||
self.out_short = self.short_trailing_stop_price * (1 + self.trailing_stop_percent)
|
||||
# print('datetime+sig: ',dt,'空头出线',self.out_short)
|
||||
# print('datetime+sig: ',dt,'多头出线',self.out_long)
|
||||
# 跟踪出场
|
||||
if self.out_long > 0:
|
||||
if (
|
||||
self.low[0] < self.out_long
|
||||
and self.pos > 0
|
||||
and self.sl_long_price > 0
|
||||
and self.low[0] > self.sl_long_price
|
||||
):
|
||||
print(
|
||||
"--多头止盈出场datetime+sig: ",
|
||||
dt,
|
||||
"Trailing stop triggered: Closing position",
|
||||
"TR",
|
||||
self.out_long,
|
||||
"low",
|
||||
self.low[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
if self.out_short > 0:
|
||||
if (
|
||||
self.high[0] > self.out_short
|
||||
and self.pos < 0
|
||||
and self.sl_shor_price > 0
|
||||
and self.high[0] < self.sl_shor_price
|
||||
):
|
||||
print(
|
||||
"--空头止盈出场datetime+sig: ",
|
||||
dt,
|
||||
"Trailing stop triggered: Closing position: ",
|
||||
"TR",
|
||||
self.out_short,
|
||||
"high",
|
||||
self.high[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_shor = 0
|
||||
self.pos = 0
|
||||
|
||||
# 固定止损
|
||||
self.fixed_stop_loss_L = self.sl_long_price * (1 - self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_long_price > 0
|
||||
and self.fixed_stop_loss_L > 0
|
||||
and self.pos > 0
|
||||
and self.closes[0] < self.fixed_stop_loss_L
|
||||
):
|
||||
print(
|
||||
"--多头止损datetime+sig: ",
|
||||
dt,
|
||||
"Fixed stop loss triggered: Closing position",
|
||||
"SL",
|
||||
self.fixed_stop_loss_L,
|
||||
"close",
|
||||
self.closes[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
self.fixed_stop_loss_S = self.sl_shor_price * (1 + self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_shor_price > 0
|
||||
and self.fixed_stop_loss_S > 0
|
||||
and self.pos < 0
|
||||
and self.closes[0] > self.fixed_stop_loss_S
|
||||
):
|
||||
print(
|
||||
"--空头止损datetime+sig: ",
|
||||
dt,
|
||||
"Fixed stop loss triggered: Closing position",
|
||||
"SL",
|
||||
self.fixed_stop_loss_S,
|
||||
"close",
|
||||
self.closes[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
# 更新最高价和最低价的列表
|
||||
self.datetime_list.append(dt)
|
||||
self.high_list.append(self.data.high[0])
|
||||
self.low_list.append(self.data.low[0])
|
||||
self.close_list.append(self.data.close[0])
|
||||
self.opens_list.append(self.data.open[0])
|
||||
self.deltas_list.append(self.data.delta[0])
|
||||
# 计算delta累计
|
||||
self.delta_cumsum.append(sum(self.deltas_list))
|
||||
|
||||
# 将当前行数据添加到 DataFrame
|
||||
# new_row = {
|
||||
# 'datetime': dt,
|
||||
# 'high': self.data.high[0],
|
||||
# 'low': self.data.low[0],
|
||||
# 'close': self.data.close[0],
|
||||
# 'open': self.data.open[0],
|
||||
# 'delta': self.data.delta[0],
|
||||
# 'delta_cumsum': sum(self.deltas_list)
|
||||
# }
|
||||
# # 使用pandas.concat代替append
|
||||
# self.df = pd.concat([self.df, pd.DataFrame([new_row])], ignore_index=True)
|
||||
|
||||
# # 检查文件是否存在
|
||||
# csv_file_path = f"output.csv"
|
||||
# if os.path.exists(csv_file_path):
|
||||
# # 仅保存最后一行数据
|
||||
# self.df.tail(1).to_csv(csv_file_path, mode='a', header=False, index=False)
|
||||
# else:
|
||||
# # 创建新文件并保存整个DataFrame
|
||||
# self.df.to_csv(csv_file_path, index=False)
|
||||
|
||||
#
|
||||
if run_kg is False: #
|
||||
# ————jerome注释:增加Boll指标测试
|
||||
upper, middle, lower = tb.BBANDS(np.array(self.deltas_list), timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
|
||||
upper_cum, middle_cum, lower_cum = tb.BBANDS(
|
||||
np.array(self.delta_cumsum), timeperiod=20, nbdevup=2, nbdevdn=2, matype=0
|
||||
)
|
||||
# 增加PPO指标测试
|
||||
# ppo = tb.PPO(np.array(self.deltas_list))
|
||||
# PPO_cum = tb.PPO(np.array(self.delta_cumsum))
|
||||
# 增加MOM指标测试
|
||||
# mom = tb.MOM(np.array(self.deltas_list), 30)
|
||||
# mom_cum = tb.MOM(np.array(self.delta_cumsum), 30)
|
||||
# 增加 MACDFIX指标测试
|
||||
# macdfix = tb.MACDFIX(np.array(self.deltas_list), 9)
|
||||
# macdfix_cum = tb.MACDFIX(np.array(self.delta_cumsum), 9)
|
||||
# 增加 CMO指标测试
|
||||
# cmo = tb.CMO(np.array(self.deltas_list))
|
||||
# cmo_cum = tb.CMO(np.array(self.delta_cumsum))
|
||||
# 增加 APO指标测试
|
||||
# apo = tb.APO(np.array(self.deltas_list))
|
||||
# apo_cum = tb.APO(np.array(self.delta_cumsum))
|
||||
# 增加 WMA指标测试
|
||||
# wma = tb.WMA(np.array(self.deltas_list))
|
||||
# wma_cum = tb.WMA(np.array(self.delta_cumsum))
|
||||
# 增加 WMA指标测试
|
||||
# ema = tb.EMA(np.array(self.deltas_list))
|
||||
# ema_cum = tb.EMA(np.array(self.delta_cumsum))
|
||||
# 增加 T3指标测试(效果非常好)
|
||||
# t3 = tb.T3(np.array(self.deltas_list))
|
||||
# t3_cum = tb.T3(np.array(self.delta_cumsum))
|
||||
# ————jerome注释:增加Boll函数测试
|
||||
# jerome注释:self.signal[0] >1 1为堆积信号
|
||||
# 开多组合= self.rinei_mean>0 and self.closes[0]>self.rinei_mean and self.signal[0] >1 and self.data.delta[0]>middle[-1] and self.delta_cumsum[-1]>middle_cum[-1]#jerome注释:self.signal[0] >1 1为堆积信号,and self.data.delta[0]>0 and self.delta_cumsum[-1]>2000
|
||||
# 开空组合= self.rinei_mean>0 and self.closes[0]<self.rinei_mean and self.signal[0] <-1 and self.data.delta[0]<middle[-1] and self.delta_cumsum[-1]<middle_cum[-1]#jerome注释:self.signal[0] >1 1为堆积信号,and self.data.delta[0]<-0 and self.delta_cumsum[-1]<-2000
|
||||
开多组合 = (
|
||||
self.reniei_bop[-1] > 0
|
||||
and self.signal[0] > 2
|
||||
and self.data.delta[0] > max(self.deltas_list[-30:-1], default=0) # 1.1 * middle[-1]
|
||||
and self.delta_cumsum[-1] > max(self.delta_cumsum[-31:-2], default=0) # 1.1 * middle_cum[-1]
|
||||
# and apo[-1] > 10
|
||||
# and apo_cum[-1] > 10
|
||||
)
|
||||
开空组合 = (
|
||||
self.reniei_bop[-1] < 0
|
||||
and self.signal[0] < -2
|
||||
and self.data.delta[0] < min(self.deltas_list[-30:-1], default=0) # 0.9 * middle[-1]
|
||||
and self.delta_cumsum[-1] < min(self.delta_cumsum[-31:-2], default=0) # 0.9 * middle_cum[-1]
|
||||
# and apo[-1] < -10
|
||||
# and apo_cum[-1] < -10
|
||||
)
|
||||
平多条件 = self.pos < 0 and self.signal[0] > 2
|
||||
平空条件 = self.pos > 0 and self.signal[0] < -2
|
||||
if self.pos != 1: #
|
||||
if 平多条件:
|
||||
# print('datetime+sig: ', dt, 'Fixed stop loss triggered: Closing position', 'SL', self.fixed_stop_loss_S, 'close', self.closes[0])
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
if 开多组合: #
|
||||
self.buy(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = 1
|
||||
self.long_trailing_stop_price = self.low[0]
|
||||
self.sl_long_price = self.data.open[0]
|
||||
# print('datetime+sig: ',dt,' sig: ',self.signal[0],'保存多头价格: ',self.long_trailing_stop_price)
|
||||
|
||||
if self.pos != -1: #
|
||||
if 平空条件:
|
||||
# print('datetime+sig: ', dt, 'Fixed stop loss triggered: Closing position', 'SL', self.fixed_stop_loss_L, 'close', self.closes[0])
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
if 开空组合: #
|
||||
self.sell(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = -1
|
||||
self.short_trailing_stop_price = self.high[0]
|
||||
self.sl_shor_price = self.data.open[0]
|
||||
# print('datetime+sig: ',dt,' sig: ',self.signal[0],'保存空头价格: ',self.short_trailing_stop_price)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 公众号:松鼠Quant
|
||||
# 主页:www.quant789.com
|
||||
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
# 版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
# 创建Cerebro实例
|
||||
|
||||
cerebro = bt.Cerebro()
|
||||
# 数据
|
||||
csv_file = r"D:\BaiduNetdiskDownload\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IF888\IF888_rs_2022_5T_back_ofdata_dj.csv" #
|
||||
png_filepath = r"D:\BaiduNetdiskDownload\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IF888\部分回测报告"
|
||||
# 从CSV文件加载数据
|
||||
data = GenericCSV_SIG(
|
||||
dataname=csv_file,
|
||||
fromdate=datetime(2022, 1, 1),
|
||||
todate=datetime(2022, 12, 29),
|
||||
timeframe=bt.TimeFrame.Minutes,
|
||||
nullvalue=0.0,
|
||||
dtformat="%Y-%m-%d %H:%M:%S",
|
||||
datetime=0,
|
||||
high=3,
|
||||
low=4,
|
||||
open=2,
|
||||
close=1,
|
||||
volume=5,
|
||||
openinterest=None,
|
||||
sig=6,
|
||||
delta=8,
|
||||
)
|
||||
|
||||
# 添加数据到Cerebro实例
|
||||
cerebro.adddata(data)
|
||||
|
||||
# 添加策略到Cerebro实例
|
||||
cerebro.addstrategy(MyStrategy_固定止损_跟踪止盈)
|
||||
|
||||
# 添加观察者和分析器到Cerebro实例
|
||||
# cerebro.addobserver(bt.observers.BuySell)
|
||||
cerebro.addobserver(bt.observers.Value)
|
||||
cerebro.addanalyzer(bt.analyzers.Returns, _name="returns")
|
||||
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")
|
||||
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="trades")
|
||||
# cerebro.addanalyzer(bt.analyzers.sharpe, __name_= "sharpe")
|
||||
初始资金 = 300000
|
||||
cerebro.broker.setcash(初始资金) # 设置初始资金
|
||||
|
||||
# 手续费,单手保证金,合约倍数
|
||||
cerebro.broker.setcommission(commission=14, margin=150000.0, mult=300) # 回测参数
|
||||
|
||||
# 运行回测
|
||||
result = cerebro.run()
|
||||
|
||||
# 获取策略分析器中的结果
|
||||
analyzer = result[0].analyzers
|
||||
total_trades = analyzer.trades.get_analysis()["total"]["total"]
|
||||
winning_trades = analyzer.trades.get_analysis()["won"]["total"]
|
||||
# 获取TradeAnalyzer分析器的结果
|
||||
trade_analyzer_result = analyzer.trades.get_analysis()
|
||||
# 获取总收益额
|
||||
total_profit = trade_analyzer_result.pnl.net.total
|
||||
|
||||
if total_trades > 0:
|
||||
win_rate = winning_trades / total_trades
|
||||
else:
|
||||
win_rate = 0.0
|
||||
# 打印回测报告
|
||||
print("回测报告:")
|
||||
print("期初权益", 初始资金)
|
||||
print("期末权益", 初始资金 + round(total_profit))
|
||||
print("盈亏额", round(total_profit))
|
||||
print("最大回撤率,", round(analyzer.drawdown.get_analysis()["drawdown"], 2), "%")
|
||||
print("胜率,", round(win_rate * 100, 2), "%")
|
||||
print("交易次数,", total_trades)
|
||||
print("盈利次数,", winning_trades)
|
||||
print("亏损次数,", total_trades - winning_trades)
|
||||
print("总手续费+滑点,", 手续费汇总)
|
||||
手续费汇总 = 0
|
||||
|
||||
# 保存回测图像文件
|
||||
plot = cerebro.plot()[0][0]
|
||||
plot_filename = os.path.splitext(os.path.basename(csv_file))[0] + "ss" + "_plot.png"
|
||||
# plot_path = os.path.join('部分回测报告', plot_filename)
|
||||
if not os.path.exists(png_filepath):
|
||||
# os.mkdir(png_filepath)
|
||||
os.makedirs(png_filepath)
|
||||
plot_path = os.path.join(png_filepath, plot_filename)
|
||||
plot.savefig(plot_path)
|
||||
|
||||
|
||||
# 公众号:松鼠Quant
|
||||
# 主页:www.quant789.com
|
||||
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
@@ -0,0 +1,514 @@
|
||||
"""
|
||||
以下是代码的详细说明:
|
||||
|
||||
#公众号:松鼠Quant
|
||||
#主页:www.quant789.com
|
||||
#本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
#版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
1.
|
||||
导入必要的模块和库:
|
||||
backtrader 用于回测功能
|
||||
datetime 用于处理日期和时间
|
||||
GenericCSVData 用于从CSV文件加载数据
|
||||
numpy 用于数值操作
|
||||
time 用于时间相关操作
|
||||
matplotlib.pyplot 用于绘图
|
||||
|
||||
2. 定义自定义手续费模板MyCommission
|
||||
继承自bt.CommInfoBase
|
||||
|
||||
3.
|
||||
定义自定义数据源类 GenericCSV_SIG:
|
||||
继承自 GenericCSVData,并添加了两个额外的行:'sig'和'delta'
|
||||
定义了参数 'sig'和'delta'
|
||||
|
||||
4.
|
||||
定义 MyStrategy_固定止损_跟踪止盈 类:
|
||||
继承自 bt.Strategy(backtrader的基础策略类)
|
||||
定义了两个参数:trailing_stop_percent 和 fixed_stop_loss_percent
|
||||
初始化策略并设置各种变量和指标
|
||||
实现了 next 方法,该方法在数据源的每个新的K线出现时被调用
|
||||
根据当前K线数据更新跟踪止盈价格
|
||||
实现了跟踪止盈出场和固定止损出场
|
||||
根据信号处理多头和空头仓位
|
||||
在策略执行过程中打印调试信息
|
||||
|
||||
5.
|
||||
if __name__ == "__main__": 代码块:
|
||||
使用 Cerebro 实例设置回测环境
|
||||
使用 GenericCSV_SIG 数据源从CSV文件加载数据
|
||||
将数据源和策略添加到 Cerebro 实例中
|
||||
添加观察者和分析器以评估性能
|
||||
设置初始资金和经纪人参数
|
||||
运行回测并获取结果
|
||||
打印回测报告,包括收益率、回撤、胜率和交易统计数据
|
||||
|
||||
6.使用前事项:
|
||||
1、主程序中修改ofdata_dj文件地址、png_filepath地址
|
||||
2、修改clearing_time2_start、clearing_time2_stop
|
||||
3、修改交易参数:lots、跟踪止损百分、固定止损百分比、duiji、cout_delta、delta
|
||||
4、修改资金参数:初始资金;回测参数:回测时间段、佣金、单边保证金、手续费;
|
||||
"""
|
||||
|
||||
import backtrader as bt
|
||||
from datetime import datetime
|
||||
from datetime import time as s_time
|
||||
from backtrader.feeds import GenericCSVData
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import time
|
||||
import matplotlib.pyplot as plt
|
||||
import os
|
||||
|
||||
import talib as tb # jerom注释: 增加talib库
|
||||
|
||||
手续费汇总 = 0
|
||||
|
||||
|
||||
class GenericCSV_SIG(GenericCSVData):
|
||||
# 从基类继承,添加一个 'sig',‘delta’行
|
||||
lines = ("sig", "delta")
|
||||
# 添加参数为从基类继承的参数
|
||||
params = (("sig", 6), ("delta", 8))
|
||||
|
||||
|
||||
class MyStrategy_固定止损_跟踪止盈(bt.Strategy):
|
||||
params = (
|
||||
("trailing_stop_percent", 0.010), # 跟踪止盈百分比
|
||||
("fixed_stop_loss_percent", 0.02), # 固定止损百分比
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.Lots = 1 # 下单手数
|
||||
|
||||
self.signal = self.datas[0].sig # 使用sig字段作为策略的信号字段
|
||||
self.delta = self.datas[0].delta
|
||||
# 获取数据序列别名列表
|
||||
line_aliases = self.datas[0].getlinealiases()
|
||||
self.pos = 0
|
||||
print(line_aliases)
|
||||
self.high = self.datas[0].high
|
||||
self.low = self.datas[0].low
|
||||
self.closes = self.datas[0].close
|
||||
self.open = self.datas[0].open
|
||||
self.trailing_stop_percent = self.params.trailing_stop_percent
|
||||
self.short_trailing_stop_price = 0
|
||||
self.long_trailing_stop_price = 0
|
||||
self.fixed_stop_loss_percent = self.params.fixed_stop_loss_percent
|
||||
self.sl_long_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_long = 0
|
||||
# 240884432
|
||||
self.out_short = 0
|
||||
self.rinei_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.rinei_mean = 0
|
||||
self.reniei_sar = []
|
||||
|
||||
self.datetime_list = []
|
||||
self.high_list = []
|
||||
self.low_list = []
|
||||
self.close_list = []
|
||||
self.opens_list = []
|
||||
self.deltas_list = []
|
||||
self.delta_cumsum = []
|
||||
|
||||
self.barN = 0
|
||||
self.df = pd.DataFrame(columns=["datetime", "high", "low", "close", "open", "delta", "delta_cumsum"])
|
||||
|
||||
self.trader_df = pd.DataFrame(columns=["open", "high", "low", "close", "volume", "openInterest", "delta"])
|
||||
|
||||
def log(self, txt, dt=None):
|
||||
"""可选,构建策略打印日志的函数:可用于打印订单记录或交易记录等"""
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print("%s, %s" % (dt.isoformat(), txt))
|
||||
|
||||
def notify_order(self, order):
|
||||
# 未被处理的订单
|
||||
if order.status in [order.Submitted, order.Accepted]:
|
||||
return
|
||||
# 已经处理的订单
|
||||
if order.status in [order.Completed, order.Canceled, order.Margin]:
|
||||
global 手续费汇总
|
||||
if order.isbuy():
|
||||
手续费汇总 += order.executed.comm
|
||||
self.log(
|
||||
"BUY EXECUTED, 订单编号:%.0f,成交价格: %.2f, 手续费滑点:%.2f, 成交量: %.2f, 品种: %s,手续费汇总:%.2f"
|
||||
% (
|
||||
order.ref, # 订单编号
|
||||
order.executed.price, # 成交价
|
||||
order.executed.comm, # 佣金
|
||||
order.executed.size, # 成交量
|
||||
order.data._name, # 品种名称
|
||||
手续费汇总,
|
||||
)
|
||||
)
|
||||
|
||||
else: # Sell
|
||||
手续费汇总 += order.executed.comm
|
||||
self.log(
|
||||
"SELL EXECUTED, 订单编号:%.0f,成交价格: %.2f, 手续费滑点:%.2f, 成交量: %.2f, 品种: %s,手续费汇总:%.2f"
|
||||
% (
|
||||
order.ref,
|
||||
order.executed.price,
|
||||
order.executed.comm,
|
||||
order.executed.size,
|
||||
order.data._name,
|
||||
手续费汇总,
|
||||
)
|
||||
)
|
||||
|
||||
def next(self):
|
||||
# 公众号:松鼠Quant
|
||||
# 主页:www.quant789.com
|
||||
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
# 版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
|
||||
# bar线计数初始化
|
||||
self.barN += 1
|
||||
position = self.getposition(self.datas[0]).size
|
||||
# 时间轴
|
||||
dt = bt.num2date(self.data.datetime[0])
|
||||
|
||||
# 更新跟踪止损价格
|
||||
def 每日重置数据():
|
||||
# 获取当前时间
|
||||
current_time = dt.time()
|
||||
# print(current_time)
|
||||
# 设置清仓操作的时间范围1:14:55到15:00
|
||||
clearing_time1_start = s_time(14, 55)
|
||||
clearing_time1_end = s_time(15, 0)
|
||||
|
||||
# 设置清仓操作的时间范围2:00:55到01:00
|
||||
clearing_time2_start = s_time(2, 25)
|
||||
clearing_time2_end = s_time(2, 30)
|
||||
|
||||
# 创建一个标志变量
|
||||
clearing_executed = False
|
||||
|
||||
if clearing_time1_start <= current_time <= clearing_time1_end and not clearing_executed:
|
||||
clearing_executed = True # 设置标志变量为已执行
|
||||
self.rinei_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
elif clearing_time2_start <= current_time <= clearing_time2_end and not clearing_executed:
|
||||
clearing_executed = True # 设置标志变量为已执行
|
||||
self.rinei_ma = []
|
||||
self.renei_high_ma = []
|
||||
self.renei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
# 如果不在任何时间范围内,可以执行其他操作
|
||||
else:
|
||||
self.rinei_ma.append(self.closes[0])
|
||||
self.renei_high_ma.append(self.high[0])
|
||||
self.renei_low_ma.append(self.low[0])
|
||||
self.rinei_mean = np.mean(self.rinei_ma)
|
||||
self.reniei_sar = tb.SAR(np.array(self.renei_high_ma), np.array(self.renei_low_ma), 0.02, 0.2)
|
||||
# self.delta_cumsum=[]
|
||||
# self.deltas_list=[]
|
||||
# print('rinei_ma',self.rinei_ma)
|
||||
clearing_executed = False
|
||||
pass
|
||||
|
||||
return clearing_executed
|
||||
|
||||
run_kg = 每日重置数据()
|
||||
|
||||
# 过滤成交量为0或小于0
|
||||
if self.data.volume[0] <= 0:
|
||||
return
|
||||
# print(f'volume,{self.data.volume[0]}')
|
||||
if self.long_trailing_stop_price > 0 and self.pos > 0:
|
||||
# print('datetime+sig: ',dt,'旧多头出线',self.long_trailing_stop_price,'low',self.low[0])
|
||||
|
||||
self.long_trailing_stop_price = (
|
||||
self.low[0] if self.long_trailing_stop_price < self.low[0] else self.long_trailing_stop_price
|
||||
)
|
||||
|
||||
# print('datetime+sig: ',dt,'多头出线',self.long_trailing_stop_price)
|
||||
if self.short_trailing_stop_price > 0 and self.pos < 0:
|
||||
# print('datetime+sig: ',dt,'旧空头出线',self.short_trailing_stop_price,'high',self.high[0])
|
||||
|
||||
self.short_trailing_stop_price = (
|
||||
self.high[0] if self.high[0] < self.short_trailing_stop_price else self.short_trailing_stop_price
|
||||
)
|
||||
|
||||
# print('datetime+sig: ',dt,'空头出线',self.short_trailing_stop_price)
|
||||
|
||||
self.out_long = self.long_trailing_stop_price * (1 - self.trailing_stop_percent)
|
||||
self.out_short = self.short_trailing_stop_price * (1 + self.trailing_stop_percent)
|
||||
# print('datetime+sig: ',dt,'空头出线',self.out_short)
|
||||
# print('datetime+sig: ',dt,'多头出线',self.out_long)
|
||||
# 跟踪出场
|
||||
if self.out_long > 0:
|
||||
if (
|
||||
self.low[0] < self.out_long
|
||||
and self.pos > 0
|
||||
and self.sl_long_price > 0
|
||||
and self.low[0] > self.sl_long_price
|
||||
):
|
||||
print(
|
||||
"--多头止盈出场datetime+sig: ",
|
||||
dt,
|
||||
"Trailing stop triggered: Closing position",
|
||||
"TR",
|
||||
self.out_long,
|
||||
"low",
|
||||
self.low[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
if self.out_short > 0:
|
||||
if (
|
||||
self.high[0] > self.out_short
|
||||
and self.pos < 0
|
||||
and self.sl_shor_price > 0
|
||||
and self.high[0] < self.sl_shor_price
|
||||
):
|
||||
print(
|
||||
"--空头止盈出场datetime+sig: ",
|
||||
dt,
|
||||
"Trailing stop triggered: Closing position: ",
|
||||
"TR",
|
||||
self.out_short,
|
||||
"high",
|
||||
self.high[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_shor = 0
|
||||
self.pos = 0
|
||||
|
||||
# 固定止损
|
||||
self.fixed_stop_loss_L = self.sl_long_price * (1 - self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_long_price > 0
|
||||
and self.fixed_stop_loss_L > 0
|
||||
and self.pos > 0
|
||||
and self.closes[0] < self.fixed_stop_loss_L
|
||||
):
|
||||
print(
|
||||
"--多头止损datetime+sig: ",
|
||||
dt,
|
||||
"Fixed stop loss triggered: Closing position",
|
||||
"SL",
|
||||
self.fixed_stop_loss_L,
|
||||
"close",
|
||||
self.closes[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
self.fixed_stop_loss_S = self.sl_shor_price * (1 + self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_shor_price > 0
|
||||
and self.fixed_stop_loss_S > 0
|
||||
and self.pos < 0
|
||||
and self.closes[0] > self.fixed_stop_loss_S
|
||||
):
|
||||
print(
|
||||
"--空头止损datetime+sig: ",
|
||||
dt,
|
||||
"Fixed stop loss triggered: Closing position",
|
||||
"SL",
|
||||
self.fixed_stop_loss_S,
|
||||
"close",
|
||||
self.closes[0],
|
||||
)
|
||||
self.close(data=self.data, price=self.data.close[0], size=self.Lots, exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
# 更新最高价和最低价的列表
|
||||
self.datetime_list.append(dt)
|
||||
self.high_list.append(self.data.high[0])
|
||||
self.low_list.append(self.data.low[0])
|
||||
self.close_list.append(self.data.close[0])
|
||||
self.opens_list.append(self.data.open[0])
|
||||
self.deltas_list.append(self.data.delta[0])
|
||||
# 计算delta累计
|
||||
self.delta_cumsum.append(sum(self.deltas_list))
|
||||
|
||||
# 将当前行数据添加到 DataFrame
|
||||
# new_row = {
|
||||
# 'datetime': dt,
|
||||
# 'high': self.data.high[0],
|
||||
# 'low': self.data.low[0],
|
||||
# 'close': self.data.close[0],
|
||||
# 'open': self.data.open[0],
|
||||
# 'delta': self.data.delta[0],
|
||||
# 'delta_cumsum': sum(self.deltas_list)
|
||||
# }
|
||||
# # 使用pandas.concat代替append
|
||||
# self.df = pd.concat([self.df, pd.DataFrame([new_row])], ignore_index=True)
|
||||
|
||||
# # 检查文件是否存在
|
||||
# csv_file_path = f"output.csv"
|
||||
# if os.path.exists(csv_file_path):
|
||||
# # 仅保存最后一行数据
|
||||
# self.df.tail(1).to_csv(csv_file_path, mode='a', header=False, index=False)
|
||||
# else:
|
||||
# # 创建新文件并保存整个DataFrame
|
||||
# self.df.to_csv(csv_file_path, index=False)
|
||||
|
||||
#
|
||||
if run_kg == False: #
|
||||
# ————jerome注释:增加Boll函数测试
|
||||
upper, middle, lower = tb.BBANDS(np.array(self.deltas_list), timeperiod=5, nbdevup=2, nbdevdn=2, matype=0)
|
||||
upper_cum, middle_cum, lower_cum = tb.BBANDS(
|
||||
np.array(self.delta_cumsum), timeperiod=5, nbdevup=2, nbdevdn=2, matype=0
|
||||
)
|
||||
# ————jerome注释:增加Boll函数测试
|
||||
# jerome注释:self.signal[0] >1 1为堆积信号
|
||||
# 开多组合= self.rinei_mean>0 and self.closes[0]>self.rinei_mean and self.signal[0] >1 and self.data.delta[0]>middle[-1] and self.delta_cumsum[-1]>middle_cum[-1]#jerome注释:self.signal[0] >1 1为堆积信号,and self.data.delta[0]>0 and self.delta_cumsum[-1]>2000
|
||||
# 开空组合= self.rinei_mean>0 and self.closes[0]<self.rinei_mean and self.signal[0] <-1 and self.data.delta[0]<middle[-1] and self.delta_cumsum[-1]<middle_cum[-1]#jerome注释:self.signal[0] >1 1为堆积信号,and self.data.delta[0]<-0 and self.delta_cumsum[-1]<-2000
|
||||
开多组合 = (
|
||||
self.reniei_sar[-1] > self.closes[0]
|
||||
and self.signal[0] > 3
|
||||
and self.data.delta[0] > 700000
|
||||
and self.delta_cumsum[-1] > 700000
|
||||
)
|
||||
开空组合 = (
|
||||
self.reniei_sar[-1] < self.closes[0]
|
||||
and self.signal[0] < -3
|
||||
and self.data.delta[0] < -700000
|
||||
and self.delta_cumsum[-1] < -700000
|
||||
)
|
||||
平多条件 = self.pos < 0 and self.signal[0] > 3
|
||||
平空条件 = self.pos > 0 and self.signal[0] < -3
|
||||
if self.pos != 1: #
|
||||
if 平多条件:
|
||||
# print('datetime+sig: ', dt, 'Fixed stop loss triggered: Closing position', 'SL', self.fixed_stop_loss_S, 'close', self.closes[0])
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
if 开多组合: #
|
||||
self.buy(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = 1
|
||||
self.long_trailing_stop_price = self.low[0]
|
||||
self.sl_long_price = self.data.open[0]
|
||||
# print('datetime+sig: ',dt,' sig: ',self.signal[0],'保存多头价格: ',self.long_trailing_stop_price)
|
||||
|
||||
if self.pos != -1: #
|
||||
if 平空条件:
|
||||
# print('datetime+sig: ', dt, 'Fixed stop loss triggered: Closing position', 'SL', self.fixed_stop_loss_L, 'close', self.closes[0])
|
||||
self.close(data=self.data, price=self.data.close[0], exectype=bt.Order.Market)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
if 开空组合: #
|
||||
self.sell(data=self.data, price=self.data.close[0], size=1, exectype=bt.Order.Market)
|
||||
self.pos = -1
|
||||
self.short_trailing_stop_price = self.high[0]
|
||||
self.sl_shor_price = self.data.open[0]
|
||||
# print('datetime+sig: ',dt,' sig: ',self.signal[0],'保存空头价格: ',self.short_trailing_stop_price)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 公众号:松鼠Quant
|
||||
# 主页:www.quant789.com
|
||||
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
# 版权归松鼠Quant所有,禁止转发、转卖源码违者必究。
|
||||
# 创建Cerebro实例
|
||||
|
||||
cerebro = bt.Cerebro()
|
||||
# 数据
|
||||
csv_file = r"D:\BaiduNetdiskDownload\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IM888\IM888_rs_2022_5T_back_ofdata_dj.csv" #
|
||||
png_filepath = r"D:\BaiduNetdiskDownload\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IM888\部分回测报告"
|
||||
# 从CSV文件加载数据
|
||||
data = GenericCSV_SIG(
|
||||
dataname=csv_file,
|
||||
fromdate=datetime(2022, 1, 1),
|
||||
todate=datetime(2022, 12, 29),
|
||||
timeframe=bt.TimeFrame.Minutes,
|
||||
nullvalue=0.0,
|
||||
dtformat="%Y-%m-%d %H:%M:%S",
|
||||
datetime=0,
|
||||
high=3,
|
||||
low=4,
|
||||
open=2,
|
||||
close=1,
|
||||
volume=5,
|
||||
openinterest=None,
|
||||
sig=6,
|
||||
delta=8,
|
||||
)
|
||||
|
||||
# 添加数据到Cerebro实例
|
||||
cerebro.adddata(data)
|
||||
|
||||
# 添加策略到Cerebro实例
|
||||
cerebro.addstrategy(MyStrategy_固定止损_跟踪止盈)
|
||||
|
||||
# 添加观察者和分析器到Cerebro实例
|
||||
# cerebro.addobserver(bt.observers.BuySell)
|
||||
cerebro.addobserver(bt.observers.Value)
|
||||
cerebro.addanalyzer(bt.analyzers.Returns, _name="returns")
|
||||
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")
|
||||
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="trades")
|
||||
# cerebro.addanalyzer(bt.analyzers.sharpe, __name_= "sharpe")
|
||||
初始资金 = 300000
|
||||
cerebro.broker.setcash(初始资金) # 设置初始资金
|
||||
|
||||
# 手续费,单手保证金,合约倍数
|
||||
cerebro.broker.setcommission(commission=14, margin=150000.0, mult=300) # 回测参数
|
||||
|
||||
# 运行回测
|
||||
result = cerebro.run()
|
||||
|
||||
# 获取策略分析器中的结果
|
||||
analyzer = result[0].analyzers
|
||||
total_trades = analyzer.trades.get_analysis()["total"]["total"]
|
||||
winning_trades = analyzer.trades.get_analysis()["won"]["total"]
|
||||
# 获取TradeAnalyzer分析器的结果
|
||||
trade_analyzer_result = analyzer.trades.get_analysis()
|
||||
# 获取总收益额
|
||||
total_profit = trade_analyzer_result.pnl.net.total
|
||||
|
||||
if total_trades > 0:
|
||||
win_rate = winning_trades / total_trades
|
||||
else:
|
||||
win_rate = 0.0
|
||||
# 打印回测报告
|
||||
print("回测报告:")
|
||||
print("期初权益", 初始资金)
|
||||
print("期末权益", 初始资金 + round(total_profit))
|
||||
print("盈亏额", round(total_profit))
|
||||
print("最大回撤率,", round(analyzer.drawdown.get_analysis()["drawdown"], 2), "%")
|
||||
print("胜率,", round(win_rate * 100, 2), "%")
|
||||
print("交易次数,", total_trades)
|
||||
print("盈利次数,", winning_trades)
|
||||
print("亏损次数,", total_trades - winning_trades)
|
||||
print("总手续费+滑点,", 手续费汇总)
|
||||
手续费汇总 = 0
|
||||
|
||||
# 保存回测图像文件
|
||||
plot = cerebro.plot()[0][0]
|
||||
plot_filename = os.path.splitext(os.path.basename(csv_file))[0] + "ss" + "_plot.png"
|
||||
# plot_path = os.path.join('部分回测报告', plot_filename)
|
||||
if not os.path.exists(png_filepath):
|
||||
# os.mkdir(png_filepath)
|
||||
os.makedirs(png_filepath)
|
||||
plot_path = os.path.join(png_filepath, plot_filename)
|
||||
plot.savefig(plot_path)
|
||||
|
||||
|
||||
# 公众号:松鼠Quant
|
||||
# 主页:www.quant789.com
|
||||
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!
|
||||
@@ -0,0 +1,679 @@
|
||||
"""
|
||||
以下是代码的详细说明:
|
||||
1.
|
||||
导入必要的模块和库:
|
||||
backtrader 用于回测功能
|
||||
datetime 用于处理日期和时间
|
||||
GenericCSVData 用于从CSV文件加载数据
|
||||
numpy 用于数值操作
|
||||
time 用于时间相关操作
|
||||
matplotlib.pyplot 用于绘图
|
||||
|
||||
2. 定义自定义手续费模板MyCommission
|
||||
继承自bt.CommInfoBase
|
||||
|
||||
3.
|
||||
定义自定义数据源类 GenericCSV_SIG:
|
||||
继承自 GenericCSVData,并添加了两个额外的行:'sig'和'delta'
|
||||
定义了参数 'sig'和'delta'
|
||||
|
||||
4.
|
||||
定义 MyStrategy_固定止损_跟踪止盈 类:
|
||||
继承自 bt.Strategy(backtrader的基础策略类)
|
||||
定义了两个参数:trailing_stop_percent 和 fixed_stop_loss_percent
|
||||
初始化策略并设置各种变量和指标
|
||||
实现了 next 方法,该方法在数据源的每个新的K线出现时被调用
|
||||
根据当前K线数据更新跟踪止盈价格
|
||||
实现了跟踪止盈出场和固定止损出场
|
||||
根据信号处理多头和空头仓位
|
||||
在策略执行过程中打印调试信息
|
||||
|
||||
5.
|
||||
if __name__ == "__main__": 代码块:
|
||||
使用 Cerebro 实例设置回测环境
|
||||
使用 GenericCSV_SIG 数据源从CSV文件加载数据
|
||||
将数据源和策略添加到 Cerebro 实例中
|
||||
添加观察者和分析器以评估性能
|
||||
设置初始资金和经纪人参数
|
||||
运行回测并获取结果
|
||||
打印回测报告,包括收益率、回撤、胜率和交易统计数据
|
||||
|
||||
6.使用前事项:
|
||||
1、主程序中修改ofdata_dj文件地址、png_filepath地址
|
||||
2、修改clearing_time2_start、clearing_time2_stop
|
||||
3、修改交易参数:lots、跟踪止损百分、固定止损百分比、duiji、cout_delta、delta
|
||||
4、修改资金参数:初始资金;回测参数:回测时间段、佣金、单边保证金、手续费;
|
||||
"""
|
||||
|
||||
import backtrader as bt
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import time as s_time
|
||||
from backtrader.feeds import GenericCSVData
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import os
|
||||
import matplotlib.pyplot as plt
|
||||
import talib as tb # jerom注释: 增加talib库
|
||||
|
||||
# import akshare as ak
|
||||
|
||||
# 下面是需要设置的参数.其他需要设置参数在函数中:每日重置数据时间、回测开始时间、结束时间、初始资金、手续费,单手保证金,合约倍数
|
||||
手续费汇总 = 0
|
||||
trailing_stop_value = 0.02
|
||||
fixed_stop_loss_value = 0.01
|
||||
deltas_windows = 240
|
||||
deltas_cum_windows = 240
|
||||
duji_value = 2
|
||||
csv_file_path = r"E:\of_data\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IM888\IM888_rs_2023_5T_back_ofdata_dj.csv"
|
||||
png_file_path = (
|
||||
r"E:\of_data\主力连续\tick生成的OF数据(5M)\data_rs_merged\中金所\IM888\部分回测报告"
|
||||
)
|
||||
start_date = datetime(2023, 1, 1)
|
||||
end_date = datetime(2023, 12, 31)
|
||||
回测资金 = 3000000
|
||||
单手手续费 = 60
|
||||
单手保证金 = 15000
|
||||
交易乘率 = 300
|
||||
|
||||
|
||||
class GenericCSV_SIG(GenericCSVData):
|
||||
# 从基类继承,添加一个 'sig',‘delta’行
|
||||
lines = ("sig", "delta")
|
||||
# 添加参数为从基类继承的参数
|
||||
params = (("sig", 6), ("delta", 8))
|
||||
|
||||
|
||||
# jerome注释增加
|
||||
def ultimate_smoother(price, period):
|
||||
# 初始化变量
|
||||
a1 = np.exp(-1.414 * np.pi / period)
|
||||
b1 = 2 * a1 * np.cos(1.414 * 180 / period)
|
||||
c2 = b1
|
||||
c3 = -a1 * a1
|
||||
c1 = (1 + c2 - c3) / 4
|
||||
|
||||
# 准备输出结果的序列
|
||||
us = np.zeros(len(price))
|
||||
|
||||
# 计算 Ultimate Smoother
|
||||
for i in range(len(price)):
|
||||
if i < 4:
|
||||
us[i] = price[i]
|
||||
else:
|
||||
us[i] = (
|
||||
(1 - c1) * price[i]
|
||||
+ (2 * c1 - c2) * price[i - 1]
|
||||
- (c1 + c3) * price[i - 2]
|
||||
+ c2 * us[i - 1]
|
||||
+ c3 * us[i - 2]
|
||||
)
|
||||
|
||||
return us
|
||||
|
||||
|
||||
class MyStrategy_固定止损_跟踪止盈(bt.Strategy):
|
||||
params = (
|
||||
("trailing_stop_percent", trailing_stop_value), # 跟踪止盈百分比
|
||||
("fixed_stop_loss_percent", fixed_stop_loss_value), # 固定止损百分比
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self.Lots = 1 # 下单手数
|
||||
|
||||
self.signal = self.datas[0].sig # 使用sig字段作为策略的信号字段
|
||||
self.delta = self.datas[0].delta
|
||||
# 获取数据序列别名列表
|
||||
line_aliases = self.datas[0].getlinealiases()
|
||||
self.pos = 0
|
||||
print(line_aliases)
|
||||
self.high = self.datas[0].high
|
||||
self.low = self.datas[0].low
|
||||
self.closes = self.datas[0].close
|
||||
self.open = self.datas[0].open
|
||||
self.trailing_stop_percent = self.params.trailing_stop_percent
|
||||
self.short_trailing_stop_price = 0
|
||||
self.long_trailing_stop_price = 0
|
||||
self.fixed_stop_loss_percent = self.params.fixed_stop_loss_percent
|
||||
self.sl_long_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_long = 0
|
||||
|
||||
self.ultimate_smoother_value = []
|
||||
|
||||
self.out_short = 0
|
||||
self.rinei_ma = []
|
||||
self.rinei_open_ma = []
|
||||
self.rinei_high_ma = []
|
||||
self.rinei_low_ma = []
|
||||
self.rinei_mean = 0
|
||||
self.rinei_T3 = []
|
||||
|
||||
self.datetime_list = []
|
||||
self.high_list = []
|
||||
self.low_list = []
|
||||
self.close_list = []
|
||||
self.opens_list = []
|
||||
self.deltas_list = []
|
||||
self.delta_cumsum = []
|
||||
self.duiji_list = []
|
||||
|
||||
self.barN = 0
|
||||
self.df = pd.DataFrame(
|
||||
columns=[
|
||||
"datetime",
|
||||
"high",
|
||||
"low",
|
||||
"close",
|
||||
"open",
|
||||
"delta",
|
||||
"delta_cumsum",
|
||||
]
|
||||
)
|
||||
|
||||
self.trader_df = pd.DataFrame(
|
||||
columns=["open", "high", "low", "close", "volume", "openInterest", "delta"]
|
||||
)
|
||||
|
||||
def log(self, txt, dt=None):
|
||||
"""可选,构建策略打印日志的函数:可用于打印订单记录或交易记录等"""
|
||||
dt = dt or self.datas[0].datetime.date(0)
|
||||
print("%s, %s" % (dt.isoformat(), txt))
|
||||
|
||||
def notify_order(self, order):
|
||||
# 未被处理的订单
|
||||
if order.status in [order.Submitted, order.Accepted]:
|
||||
return
|
||||
# 已经处理的订单
|
||||
if order.status in [order.Completed, order.Canceled, order.Margin]:
|
||||
global 手续费汇总
|
||||
if order.isbuy():
|
||||
手续费汇总 += order.executed.comm
|
||||
self.log(
|
||||
"BUY EXECUTED, 订单编号:%.0f,成交价格: %.2f, 手续费滑点:%.2f, 成交量: %.2f, 品种: %s,手续费汇总:%.2f"
|
||||
% (
|
||||
order.ref, # 订单编号
|
||||
order.executed.price, # 成交价
|
||||
order.executed.comm, # 佣金
|
||||
order.executed.size, # 成交量
|
||||
order.data._name, # 品种名称
|
||||
手续费汇总,
|
||||
)
|
||||
)
|
||||
|
||||
else: # Sell
|
||||
手续费汇总 += order.executed.comm
|
||||
self.log(
|
||||
"SELL EXECUTED, 订单编号:%.0f,成交价格: %.2f, 手续费滑点:%.2f, 成交量: %.2f, 品种: %s,手续费汇总:%.2f"
|
||||
% (
|
||||
order.ref,
|
||||
order.executed.price,
|
||||
order.executed.comm,
|
||||
order.executed.size,
|
||||
order.data._name,
|
||||
手续费汇总,
|
||||
)
|
||||
)
|
||||
|
||||
def next(self):
|
||||
# bar线计数初始化
|
||||
self.barN += 1
|
||||
# position = self.getposition(self.datas[0]).size
|
||||
# 时间轴
|
||||
dt = bt.num2date(self.data.datetime[0])
|
||||
|
||||
# 更新跟踪止损价格
|
||||
def 每日重置数据():
|
||||
# 获取当前时间
|
||||
current_time = dt.time()
|
||||
# print(current_time)
|
||||
# 设置清仓操作的时间范围1:14:55到15:00
|
||||
clearing_time1_start = s_time(14, 55)
|
||||
clearing_time1_end = s_time(15, 0)
|
||||
|
||||
# 设置清仓操作的时间范围2:00:55到01:00
|
||||
clearing_time2_start = s_time(2, 25)
|
||||
clearing_time2_end = s_time(2, 30)
|
||||
|
||||
# 创建一个标志变量
|
||||
clearing_executed = False
|
||||
|
||||
if (
|
||||
clearing_time1_start <= current_time <= clearing_time1_end
|
||||
and not clearing_executed
|
||||
):
|
||||
clearing_executed = True # 设置标志变量为已执行
|
||||
self.rinei_ma = []
|
||||
self.rinei_open_ma = []
|
||||
self.rinei_high_ma = []
|
||||
self.rinei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
self.duiji_list = []
|
||||
elif (
|
||||
clearing_time2_start <= current_time <= clearing_time2_end
|
||||
and not clearing_executed
|
||||
):
|
||||
clearing_executed = True # 设置标志变量为已执行
|
||||
self.rinei_ma = []
|
||||
self.rinei_open_ma = []
|
||||
self.rinei_high_ma = []
|
||||
self.rinei_low_ma = []
|
||||
self.delta_cumsum = []
|
||||
self.deltas_list = []
|
||||
self.duiji_list = []
|
||||
# 如果不在任何时间范围内,可以执行其他操作
|
||||
else:
|
||||
self.rinei_open_ma.append(self.open[0])
|
||||
self.rinei_ma.append(self.closes[0])
|
||||
self.rinei_high_ma.append(self.high[0])
|
||||
self.rinei_low_ma.append(self.low[0])
|
||||
self.rinei_mean = np.mean(self.rinei_ma)
|
||||
# self.rinei_T3 = tb.HT_TRENDLINE(np.array(self.rinei_ma),timeperiod=5)
|
||||
self.rinei_T3 = tb.T3(
|
||||
np.array(self.rinei_ma), 3
|
||||
) # , timeperiod=5, vfactor=0.7
|
||||
# self.riniei_bop = tb.BOP(
|
||||
# np.array(self.rinei_open_ma),
|
||||
# np.array(self.rinei_high_ma),
|
||||
# np.array(self.rinei_low_ma),
|
||||
# np.array(self.rinei_ma),
|
||||
# )
|
||||
# self.delta_cumsum=[]
|
||||
# self.deltas_list=[]
|
||||
# print('rinei_ma',self.rinei_ma)
|
||||
clearing_executed = False
|
||||
pass
|
||||
|
||||
return clearing_executed
|
||||
|
||||
run_kg = 每日重置数据()
|
||||
|
||||
# 过滤成交量为0或小于0
|
||||
if self.data.volume[0] <= 0:
|
||||
return
|
||||
# print(f'volume,{self.data.volume[0]}')
|
||||
if self.long_trailing_stop_price > 0 and self.pos > 0:
|
||||
# print('datetime+sig: ',dt,'旧多头出线',self.long_trailing_stop_price,'low',self.low[0])
|
||||
|
||||
self.long_trailing_stop_price = (
|
||||
self.low[0]
|
||||
if self.long_trailing_stop_price < self.low[0]
|
||||
else self.long_trailing_stop_price
|
||||
)
|
||||
|
||||
# print('datetime+sig: ',dt,'多头出线',self.long_trailing_stop_price)
|
||||
if self.short_trailing_stop_price > 0 and self.pos < 0:
|
||||
# print('datetime+sig: ',dt,'旧空头出线',self.short_trailing_stop_price,'high',self.high[0])
|
||||
|
||||
self.short_trailing_stop_price = (
|
||||
self.high[0]
|
||||
if self.high[0] < self.short_trailing_stop_price
|
||||
else self.short_trailing_stop_price
|
||||
)
|
||||
|
||||
# print('datetime+sig: ',dt,'空头出线',self.short_trailing_stop_price)
|
||||
|
||||
# self.out_long = self.long_trailing_stop_price * (
|
||||
# 1 - self.trailing_stop_percent)
|
||||
# self.out_short = self.short_trailing_stop_price * (
|
||||
# 1 + self.trailing_stop_percent)
|
||||
self.ultimate_smoother_value = ultimate_smoother(self.closes, 20)
|
||||
self.out_long = self.ultimate_smoother_value[-1]
|
||||
self.out_short = self.ultimate_smoother_value[-1]
|
||||
# print('datetime+sig: ',dt,'空头出线',self.out_short)
|
||||
# print('datetime+sig: ',dt,'多头出线',self.out_long)
|
||||
# 跟踪出场
|
||||
if self.out_long > 0:
|
||||
if (
|
||||
self.low[0] < self.out_long
|
||||
and self.pos > 0
|
||||
and self.sl_long_price > 0
|
||||
and self.low[0] > self.sl_long_price
|
||||
):
|
||||
print(
|
||||
"--多头止盈出场datetime+sig: ",
|
||||
dt,
|
||||
"Trailing stop triggered: Closing position",
|
||||
"TR",
|
||||
self.out_long,
|
||||
"low",
|
||||
self.low[0],
|
||||
)
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
|
||||
if self.out_short > 0:
|
||||
if (
|
||||
self.high[0] > self.out_short
|
||||
and self.pos < 0
|
||||
and self.sl_shor_price > 0
|
||||
and self.high[0] < self.sl_shor_price
|
||||
):
|
||||
print(
|
||||
"--空头止盈出场datetime+sig: ",
|
||||
dt,
|
||||
"Trailing stop triggered: Closing position: ",
|
||||
"TR",
|
||||
self.out_short,
|
||||
"high",
|
||||
self.high[0],
|
||||
)
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_shor = 0
|
||||
self.pos = 0
|
||||
|
||||
# 固定止损
|
||||
self.fixed_stop_loss_L = self.sl_long_price * (1 - self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_long_price > 0
|
||||
and self.fixed_stop_loss_L > 0
|
||||
and self.pos > 0
|
||||
and self.closes[0] < self.fixed_stop_loss_L
|
||||
):
|
||||
print(
|
||||
"--多头止损datetime+sig: ",
|
||||
dt,
|
||||
"Fixed stop loss triggered: Closing position",
|
||||
"SL",
|
||||
self.fixed_stop_loss_L,
|
||||
"close",
|
||||
self.closes[0],
|
||||
)
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
self.fixed_stop_loss_S = self.sl_shor_price * (1 + self.fixed_stop_loss_percent)
|
||||
if (
|
||||
self.sl_shor_price > 0
|
||||
and self.fixed_stop_loss_S > 0
|
||||
and self.pos < 0
|
||||
and self.closes[0] > self.fixed_stop_loss_S
|
||||
):
|
||||
print(
|
||||
"--空头止损datetime+sig: ",
|
||||
dt,
|
||||
"Fixed stop loss triggered: Closing position",
|
||||
"SL",
|
||||
self.fixed_stop_loss_S,
|
||||
"close",
|
||||
self.closes[0],
|
||||
)
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=self.Lots,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
|
||||
# 更新最高价和最低价的列表
|
||||
self.datetime_list.append(dt)
|
||||
self.high_list.append(self.data.high[0])
|
||||
self.low_list.append(self.data.low[0])
|
||||
self.close_list.append(self.data.close[0])
|
||||
self.opens_list.append(self.data.open[0])
|
||||
self.deltas_list.append(self.data.delta[0])
|
||||
# 计算delta累计
|
||||
self.delta_cumsum.append(sum(self.deltas_list[-20:]))
|
||||
self.duiji_list.append(self.data.sig[0])
|
||||
|
||||
# 将当前行数据添加到 DataFrame
|
||||
# new_row = {
|
||||
# 'datetime': dt,
|
||||
# 'high': self.data.high[0],
|
||||
# 'low': self.data.low[0],
|
||||
# 'close': self.data.close[0],
|
||||
# 'open': self.data.open[0],
|
||||
# 'delta': self.data.delta[0],
|
||||
# 'delta_cumsum': sum(self.deltas_list)
|
||||
# }
|
||||
# # 使用pandas.concat代替append
|
||||
# self.df = pd.concat([self.df, pd.DataFrame([new_row])], ignore_index=True)
|
||||
|
||||
# # 检查文件是否存在
|
||||
# csv_file_path = f"output.csv"
|
||||
# if os.path.exists(csv_file_path):
|
||||
# # 仅保存最后一行数据
|
||||
# self.df.tail(1).to_csv(csv_file_path, mode='a', header=False, index=False)
|
||||
# else:
|
||||
# # 创建新文件并保存整个DataFrame
|
||||
# self.df.to_csv(csv_file_path, index=False)
|
||||
|
||||
#
|
||||
if run_kg is False:
|
||||
# ————jerome注释:增加指标测试
|
||||
# 增加Boll指标测试
|
||||
# upper, middle, lower = tb.BBANDS(np.array(self.deltas_list), timeperiod=20, nbdevup=2, nbdevdn=2, matype=0)
|
||||
# upper_cum, middle_cum, lower_cum = tb.BBANDS(
|
||||
# np.array(self.delta_cumsum), timeperiod=20, nbdevup=2, nbdevdn=2, matype=0
|
||||
# )
|
||||
# 增加PPO指标测试
|
||||
# ppo = tb.PPO(np.array(self.deltas_list))
|
||||
# PPO_cum = tb.PPO(np.array(self.delta_cumsum))
|
||||
# 增加MOM指标测试
|
||||
# mom = tb.MOM(np.array(self.deltas_list), 30)
|
||||
# mom_cum = tb.MOM(np.array(self.delta_cumsum), 30)
|
||||
# 增加 MACDFIX指标测试
|
||||
# macdfix = tb.MACDFIX(np.array(self.deltas_list), 9)
|
||||
# macdfix_cum = tb.MACDFIX(np.array(self.delta_cumsum), 9)
|
||||
# 增加 CMO指标测试
|
||||
# cmo = tb.CMO(np.array(self.deltas_list))
|
||||
# cmo_cum = tb.CMO(np.array(self.delta_cumsum))
|
||||
# 增加 APO指标测试
|
||||
# apo = tb.APO(np.array(self.deltas_list))
|
||||
# apo_cum = tb.APO(np.array(self.delta_cumsum))
|
||||
# 增加 WMA指标测试
|
||||
# wma = tb.WMA(np.array(self.deltas_list))
|
||||
# wma_cum = tb.WMA(np.array(self.delta_cumsum))
|
||||
# 增加 WMA指标测试
|
||||
# ema = tb.EMA(np.array(self.deltas_list))
|
||||
# ema_cum = tb.EMA(np.array(self.delta_cumsum))
|
||||
# 增加 T3指标测试(效果非常好)
|
||||
t3 = tb.T3(np.array(self.deltas_list))
|
||||
t3_cum = tb.T3(np.array(self.delta_cumsum))
|
||||
# futures_main_sina_hist = ak.futures_main_sina(
|
||||
# symbol="IM0", start_date=start_date, end_date="20231231"
|
||||
# )
|
||||
# futures_main_sina_hist["5day_ma"] = (
|
||||
# futures_main_sina_hist["收盘价"].rolling(window=5).mean()
|
||||
# )
|
||||
# ——jerome注释:self.signal[0] >1 1为堆积信号
|
||||
# 开多组合= self.rinei_mean>0 and self.closes[0]>self.rinei_mean and self.signal[0] >1 and self.data.delta[0]>middle[-1] and self.delta_cumsum[-1]>middle_cum[-1]#jerome注释:self.signal[0] >1 1为堆积信号,and self.data.delta[0]>0 and self.delta_cumsum[-1]>2000
|
||||
# 开空组合= self.rinei_mean>0 and self.closes[0]<self.rinei_mean and self.signal[0] <-1 and self.data.delta[0]<middle[-1] and self.delta_cumsum[-1]<middle_cum[-1]#jerome注释:self.signal[0] >1 1为堆积信号,and self.data.delta[0]<-0 and self.delta_cumsum[-1]<-2000
|
||||
开多组合 = (
|
||||
self.closes[0] > self.rinei_T3[-1]
|
||||
# futures_main_sina_hist["收盘价"].iloc[0]
|
||||
# > futures_main_sina_hist["5day_ma"].iloc[-1]
|
||||
# self.riniei_bop[-1] > 0
|
||||
and self.signal[0]
|
||||
> max(self.duiji_list[-deltas_windows:-1], default=0) # duji_value
|
||||
and self.data.delta[0] > t3[-1] # max(
|
||||
# self.deltas_list[-deltas_windows:-1], default=0
|
||||
# ) # 1.1 * middle[-1]
|
||||
and self.delta_cumsum[-1] > t3_cum[-1] # max(
|
||||
# self.delta_cumsum[-deltas_cum_windows - 1 : -2], default=0
|
||||
# ) # 1.1 * middle_cum[-1]
|
||||
# and apo[-1] > 10
|
||||
# and apo_cum[-1] > 10
|
||||
)
|
||||
开空组合 = (
|
||||
self.closes[0] < self.rinei_T3[-1]
|
||||
# self.riniei_bop[-1] < 0
|
||||
# futures_main_sina_hist["收盘价"].iloc[0]
|
||||
# < futures_main_sina_hist["5day_ma"].iloc[-1]
|
||||
and self.signal[0]
|
||||
< min(self.duiji_list[-deltas_windows:-1], default=0) # -duji_value
|
||||
and self.data.delta[0] < t3[-1] # min(
|
||||
# self.deltas_list[-deltas_windows:-1], default=0
|
||||
# ) # 0.9 * middle[-1]
|
||||
and self.delta_cumsum[-1] < t3_cum[-1] # min(
|
||||
# self.delta_cumsum[-deltas_cum_windows - 1 : -2], default=0
|
||||
# ) # 0.9 * middle_cum[-1]
|
||||
# and apo[-1] < -10
|
||||
# and apo_cum[-1] < -10
|
||||
)
|
||||
平空条件 = self.pos < 0 and self.signal[0] > max(
|
||||
self.duiji_list[-deltas_windows:-1], default=0
|
||||
) # duji_value
|
||||
平多条件 = self.pos > 0 and self.signal[0] < min(
|
||||
self.duiji_list[-deltas_windows:-1], default=0
|
||||
) # -duji_value
|
||||
if self.pos != 1: #
|
||||
if 平空条件:
|
||||
# print('datetime+sig: ', dt, 'Fixed stop loss triggered: Closing position', 'SL', self.fixed_stop_loss_S, 'close', self.closes[0])
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.short_trailing_stop_price = 0
|
||||
self.sl_shor_price = 0
|
||||
self.out_short = 0
|
||||
self.pos = 0
|
||||
if 开多组合: #
|
||||
self.buy(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=1,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.pos = 1
|
||||
self.long_trailing_stop_price = self.low[0]
|
||||
self.sl_long_price = self.data.open[0]
|
||||
# print('datetime+sig: ',dt,' sig: ',self.signal[0],'保存多头价格: ',self.long_trailing_stop_price)
|
||||
|
||||
if self.pos != -1: #
|
||||
if 平多条件:
|
||||
# print('datetime+sig: ', dt, 'Fixed stop loss triggered: Closing position', 'SL', self.fixed_stop_loss_L, 'close', self.closes[0])
|
||||
self.close(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.long_trailing_stop_price = 0
|
||||
self.sl_long_price = 0
|
||||
self.out_long = 0
|
||||
self.pos = 0
|
||||
if 开空组合: #
|
||||
self.sell(
|
||||
data=self.data,
|
||||
price=self.data.close[0],
|
||||
size=1,
|
||||
exectype=bt.Order.Market,
|
||||
)
|
||||
self.pos = -1
|
||||
self.short_trailing_stop_price = self.high[0]
|
||||
self.sl_shor_price = self.data.open[0]
|
||||
# print('datetime+sig: ',dt,' sig: ',self.signal[0],'保存空头价格: ',self.short_trailing_stop_price)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 创建Cerebro实例
|
||||
|
||||
cerebro = bt.Cerebro()
|
||||
# 数据
|
||||
csv_file = csv_file_path
|
||||
png_filepath = png_file_path
|
||||
# 从CSV文件加载数据
|
||||
data = GenericCSV_SIG(
|
||||
dataname=csv_file,
|
||||
fromdate=start_date, # datetime(2023, 1, 1),
|
||||
todate=end_date, # datetime(2023, 12, 29),
|
||||
timeframe=bt.TimeFrame.Minutes,
|
||||
nullvalue=0.0,
|
||||
dtformat="%Y-%m-%d %H:%M:%S",
|
||||
datetime=0,
|
||||
high=3,
|
||||
low=4,
|
||||
open=2,
|
||||
close=1,
|
||||
volume=5,
|
||||
openinterest=None,
|
||||
sig=6,
|
||||
delta=8,
|
||||
)
|
||||
|
||||
# 添加数据到Cerebro实例
|
||||
cerebro.adddata(data)
|
||||
|
||||
# 添加策略到Cerebro实例
|
||||
cerebro.addstrategy(MyStrategy_固定止损_跟踪止盈)
|
||||
|
||||
# 添加观察者和分析器到Cerebro实例
|
||||
# cerebro.addobserver(bt.observers.BuySell)
|
||||
cerebro.addobserver(bt.observers.Value)
|
||||
cerebro.addanalyzer(bt.analyzers.Returns, _name="returns")
|
||||
cerebro.addanalyzer(bt.analyzers.DrawDown, _name="drawdown")
|
||||
cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, _name="trades")
|
||||
# cerebro.addanalyzer(bt.analyzers.sharpe, __name_= "sharpe")
|
||||
初始资金 = 回测资金
|
||||
cerebro.broker.setcash(初始资金) # 设置初始资金
|
||||
|
||||
# 手续费,单手保证金,合约倍数
|
||||
cerebro.broker.setcommission(
|
||||
commission=单手手续费, margin=单手保证金, mult=交易乘率
|
||||
) # 回测参数
|
||||
|
||||
# 运行回测
|
||||
result = cerebro.run()
|
||||
|
||||
# 获取策略分析器中的结果
|
||||
analyzer = result[0].analyzers
|
||||
total_trades = analyzer.trades.get_analysis()["total"]["total"]
|
||||
winning_trades = analyzer.trades.get_analysis()["won"]["total"]
|
||||
# 获取TradeAnalyzer分析器的结果
|
||||
trade_analyzer_result = analyzer.trades.get_analysis()
|
||||
# 获取总收益额
|
||||
total_profit = trade_analyzer_result.pnl.net.total
|
||||
|
||||
if total_trades > 0:
|
||||
win_rate = winning_trades / total_trades
|
||||
else:
|
||||
win_rate = 0.0
|
||||
# 打印回测报告
|
||||
print("回测报告:")
|
||||
print("期初权益", 初始资金)
|
||||
print("期末权益", 初始资金 + round(total_profit))
|
||||
print("盈亏额", round(total_profit))
|
||||
print("最大回撤率,", round(analyzer.drawdown.get_analysis()["drawdown"], 2), "%")
|
||||
print("胜率,", round(win_rate * 100, 2), "%")
|
||||
print("交易次数,", total_trades)
|
||||
print("盈利次数,", winning_trades)
|
||||
print("亏损次数,", total_trades - winning_trades)
|
||||
print("总手续费+滑点,", 手续费汇总)
|
||||
手续费汇总 = 0
|
||||
|
||||
# 保存回测图像文件
|
||||
plot = cerebro.plot()[0][0]
|
||||
plot_filename = os.path.splitext(os.path.basename(csv_file))[0] + "ss" + "_plot.png"
|
||||
# plot_path = os.path.join('部分回测报告', plot_filename)
|
||||
if not os.path.exists(png_filepath):
|
||||
# os.mkdir(png_filepath)
|
||||
os.makedirs(png_filepath)
|
||||
plot_path = os.path.join(png_filepath, plot_filename)
|
||||
plot.savefig(plot_path)
|
||||
Reference in New Issue
Block a user