增加交易策略、交易指标、量化库代码等文件夹

This commit is contained in:
Win_home
2025-04-27 15:54:09 +08:00
parent ca3b209096
commit f57150dae8
589 changed files with 854346 additions and 1757 deletions

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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.Strategybacktrader的基础策略类
定义了两个参数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)
# 设置清仓操作的时间范围114:55到15:00
clearing_time1_start = s_time(14, 55)
clearing_time1_end = s_time(15, 0)
# 设置清仓操作的时间范围200: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
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!

View File

@@ -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.Strategybacktrader的基础策略类
定义了两个参数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)
# 设置清仓操作的时间范围114:55到15:00
clearing_time1_start = s_time(14, 55)
clearing_time1_end = s_time(15, 0)
# 设置清仓操作的时间范围200: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
# 本策略仅作学习交流使用,实盘交易盈亏投资者个人负责!!!

View File

@@ -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.Strategybacktrader的基础策略类
定义了两个参数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)
# 设置清仓操作的时间范围114:55到15:00
clearing_time1_start = s_time(14, 55)
clearing_time1_end = s_time(15, 0)
# 设置清仓操作的时间范围200: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)