DualThrust期货经典策略在quautlab框架下的配置(代码+数据)

熟悉咱们框架的同学应该知道,咱们对于“大类资产配置”, “多资产轮动”,策略模板写起来特别快,基本不需要代码

name = "示例-资产配置-再平衡"
desc = ""
start_date = "20100101"
commission = 0.0001
slippage = 0.0001
benchmark = "000300.SH"
symbols = [ "000300.SH", "399006.SZ",]
data_folder = "index"
fields = []
names = []
[[algos]]
name = "RunWeekly"
args = []

[[algos]]
name = "SelectAll"
args = []

[[algos]]
name = "WeightEqually"
args = []

[[algos]]
name = "Rebalance"
args = []

图片

然后就有同学问了,那传统那种择时策略,逻辑可能很个性化,很复杂怎么办,咱们的框架如何支持了。

因此,今天来说说Dual Thrust策略

图片

(1)N日High的最高价HH, N日Close的最低价LC;

(2)N日Close的最高价HC,N日Low的最低价LL;

(3)Range = Max(HH-LC,HC-LL)

(4)BuyLine = Open + K1×Range

(5)SellLine = Open + K2×Range

(1)当价格向上突破上轨时,如果当时持有空仓,则先平仓,再开多仓;如果没有仓位,则直接开多仓;

(2)当价格向下突破下轨时,如果当时持有多仓,则先平仓,再开空仓;如果没有仓位,则直接开空仓;

我们的dataloader可以直接把所有指标都计算出来:

(原则上讲,多数的趋势策略,都是指标的向量化计算,都可以通过因子表达式预先计算出来,少数如网格,市值平衡,需要根据近期数据动态计算的除外)

图片

通过对proj的name和fields的配置,实现加载B0(螺纹钢)的历史数据,以及相应的通道指标,包括交易信号(long、short)都准备好了

def gen_dual_thrust():
    proj = ProjConfig()
    proj.name = 'Dual Thrust策略'
    proj.commission = 0.0001
    proj.slippage = 0.0001
    proj.symbols = ['B0']  # 证券池列表
    proj.benchmark = 'B0'
    proj.start_date = '20100101'
    proj.data_folder = 'futures'  # 这里指定data/数据目录

    fields = ["shift(max(high,10),1)", 'shift(min(close,10),1)', "shift(max(close,10),1)", 'shift(min(low,10),1)']
    fields.append('greater(HH-LC,HC-LL)')
    fields.append('open+range*0.1')
    fields.append('open-range*0.1')
    fields.append('close>buyline')
    fields.append('close<sellline')
    names = ["HH", "LC", 'HC', 'LL', 'range', 'buyline', 'sellline', 'long', 'short']
    proj.fields = fields
    proj.names = names

这里类似在线量化平台的逻辑:

图片

有了信号,那么就到咱们SelectBySignal出场了。

进一步提升咱们SelectBySignal的通用性,dirction为信号方向,long为开多单,short为开空单 ,即做空,而flat是平仓。在A股就是卖出。

class SelectBySignal(Algo):
    def __init__(self, rules=[], at_least_count=1, direction='long'):
        # direction是信号的方向: long:多单, short空单, flat平仓。
        super(SelectBySignal, self).__init__()
        self.rules = rules
        self.at_least_count = at_least_count
        self.direction = direction

    def _check_if_matched(self, df_bar, rules, at_least_count):
        matched_items = []
        for symbol in list(df_bar.index):
            bar = df_bar.loc[symbol]
            match = 0
            for i, rule in enumerate(rules):
                if rule in list(bar.index):
                    if bar[rule]:
                        match += 1
                else:
                    logger.warning('rule:{}指标未计算?'.format(rule))
            if match >= at_least_count:
                matched_items.append(symbol)
        return matched_items

    def __call__(self, target):
        df_bar = target.df_bar
        matched = None
        if self.rules and len(self.rules):
            matched = self._check_if_matched(df_bar, self.rules, self.at_least_count)
        if len(matched) == 0:
            return True

        if self.direction == 'flat':
            target.temp['selected_flat'] = matched  # 卖出信号,如果允许short,那就是做空单,或者平仓
        elif self.direction == 'short':
            target.temp['selected_short'] = matched
        else:
            target.temp['selected'] = matched
        return True

在使用上,就比较简单了:

# 这里是策略算子列表
proj.algos.append(AlgoConfig(name=SelectBySignal().name, kwargs={'rules': ['long'], 'direction': 'long'}))
proj.algos.append(AlgoConfig(name=SelectBySignal().name, kwargs={'rules': ['short'], 'direction': 'short'}))

根据long/short信号,计算当天需要开仓的多空标的。DualThrust是多空操作,没有平仓信号,也就是说,要么开多,要么开空

再下一步就是仓位计算,确定了要买,那么要买多少?就是仓位分配算子的任务了。

我们策略的逻辑是开仓就是100%,空仓也是。

class WeightEqually(Algo):
    """
    形成调仓表temp['weights'],三个交易方向,变成统一的调仓权重指令。
    我们一般不用buy/sell这种固定size来回测,因为size和你的组合市值有关,10万,还是100万,不一样。
percent就是一样的,比如单支股票持仓不超过10%

    """

    def __init__(self):
        super(WeightEqually, self).__init__()

    def __call__(self, target):
        target.temp["weights"] = {}

        if 'selected' in target.temp.keys():
            selected = target.temp["selected"]
            n = len(selected)
            if n > 0:
                w = 1.0 / n
                target.temp["weights"] = {x: w for x in selected}

        if 'selected_short' in target.temp.keys():
            selected = target.temp["selected_short"]
            n = len(selected)
            if n > 0:
                w = 1.0 / n
                target.temp["weights"].update({x: w for x in selected})

        if 'selected_flat' in target.temp.keys(): # flat直接仓位归0
            selected = target.temp["selected_flat"]
            n = len(selected)
            if n > 0:
                target.temp["weights"].update({x: 0 for x in selected})
        return True

最后就是执行调仓,只有这一块与backtrader的api相关。

最后根据调仓表来执行:

在执行中,原则上先卖后买,与你手动交易类似,先平仓才有现金买。

1、目标权重是0的,平仓;

2、目标权重与当前权重相反的,先平仓;(再开反方向单)

3、仓位绝对值变小的,也就是仓位降低的,先执行;相当于换仓的逻辑。

class Rebalance(Algo):

    def __init__(self):
        super(Rebalance, self).__init__()

    def __call__(self, target):
        if "weights" not in target.temp:
            return True

        target_weights = target.temp["weights"]
        if type(target_weights) is pd.Series:
            targets = target_weights.to_dict()

        # 这里只根据当前调仓表调仓,比如当前开多单,本期无信号的,不进行调仓,这也符合我们的常识。

        # 先执行平仓
        for s, w in target_weights.items():
            if w == 0:
                target.close(s)
                del target_weights[s]

        total_mv = target.broker.getvalue()

        to_sell = []
        to_buy = []
        for s, w in target_weights.items():
            data = self.getdatabyname(s)
            pos = self.getposition(data)
            curr_w = target.get_symbol_mv(s) / total_mv
            if w * curr_w < 0:  # 方向相反,先平仓

target.close(s)

        to_buy.append(s) # 肯定是开仓操作
        continue

if abs(w) < abs(curr_w):
to_sell.append(s)
else:
to_buy.append(s)

# 仓位降低的先执行,得到cash
for s in to_sell:
w = target_weights[s]
target.order_target_percent(s, w * 0.95)

for s in to_buy:
w = target_weights[s]
target.order_target_percent(s, w * 0.95)

return True

策略在如下位置 :

图片

name = "Dual Thrust策略"
desc = ""
start_date = "20100101"
commission = 0.0001
slippage = 0.0001
benchmark = "B0"
symbols = [ "B0",]
data_folder = "futures"
fields = [ "shift(max(high,10),1)", "shift(min(close,10),1)", "shift(max(close,10),1)", "shift(min(low,10),1)", "greater(HH-LC,HC-LL)", "open+range*0.1", "open-range*0.1", "close>buyline", "close<sellline",]
names = [ "HH", "LC", "HC", "LL", "range", "buyline", "sellline", "long", "short",]
[[algos]]
name = "SelectBySignal"
args = []

[algos.kwargs]
rules = [ "long",]
direction = "long"
[[algos]]
name = "SelectBySignal"
args = []

[algos.kwargs]
rules = [ "short",]
direction = "short"
[[algos]]
name = "WeightEqually"
args = []

[[algos]]
name = "Rebalance"
args = []

发布者:股市刺客,转载请注明出处:https://www.95sca.cn/archives/103826
站内所有文章皆来自网络转载或读者投稿,请勿用于商业用途。如有侵权、不妥之处,请联系站长并出示版权证明以便删除。敬请谅解!

(0)
股市刺客的头像股市刺客
上一篇 2024 年 7 月 29 日
下一篇 2024 年 7 月 29 日

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注