backtrader的order_target_percent源码解读(quantlab1.5版本更新,代码+数据下载)

今天解读一下backtrader的order_target_percent,目前我们的大类资产配置,轮动的模板,细心的同学可能发现了,只用了这一个下单函数。

图片

Rebalance的函数进行了优化,先卖后买:

import numpy as np
import pandas as pd

from .algo_base import Algo
from loguru import logger


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"]
        # print(targets)
        if type(target_weights) is pd.Series:
            targets = target_weights.to_dict()

        # 要清仓的
        symbols = target.get_current_holding_symbols()
        for s in symbols:
            if s not in target_weights.keys():
                # logger.info('清空:{}'.format(s))
                # logger.info('{}平仓:{}'.format(target.now, s))
                target.close(s)

        # 如果调仓就1个,且当前有持仓就1个,二者还相等,那不执行
        if len(target_weights.keys()) == 1 and list(target_weights.keys())[0] in symbols and len(symbols) == 1:
            return True

            # 目标仓位里,分成两部分:目标仓位小于当前仓位的,要卖一部分,其余的,都是买入,含新增的(curr_w=0            # to_sell / to_buy
        to_sell = []
        to_buy = []

        for s in target_weights.keys():
            # 取当前仓位比例
            mv = target.broker.getvalue()
            curr_w = target.get_symbol_mv(s) / mv
            if target_weights[s] < curr_w:
                to_sell.append(s)
            else:
                to_buy.append(s)

        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.9)


        # 这里还有一个逻辑,就是仓位变少的(卖出)要选执行,然后才是买入,否则现金会不够多。
        #for symbol, w in target_weights.items():
        #    # logger.info('{}调仓:{},{}'.format(target.now, data, w))


        return True

就是把symbol调整到目标仓位。

我们来仔细看下它的具体操作。

target *= self.broker.getvalue(),就是:目标市值=总市值*权重

possize = self.getposition(data, self.broker).size # 这一句是多余的
target *= self.broker.getvalue()

return self.order_target_value(data=data, target=target, **kwargs)

order_target_value就是仓位变成了目标市值。

possize = self.getposition(data, self.broker).size
if not target and possize:  # closing a position
    return self.close(data=data, size=possize, price=price, **kwargs)

else:
    value = self.broker.getvalue(datas=[data])
    comminfo = self.broker.getcommissioninfo(data)

    # Make sure a price is there
    price = price if price is not None else data.close[0]

    if target > value:
        size = comminfo.getsize(price, target - value)
        return self.buy(data=data, size=size, price=price, **kwargs)

    elif target < value:
        size = comminfo.getsize(price, value - target)
        return self.sell(data=data, size=size, price=price, **kwargs)

这里的逻辑也简单,就是目标市值如果大,那就买入指定size(份额),目标市值如果小,那就卖出。

buy就是提交一个“市价单“:

def buy(self, owner, data,
        size, price=None, plimit=None,
        exectype=None, valid=None, tradeid=0,
        **kwargs):

    order = BuyOrder(owner=owner, data=data,
                     size=size, price=price, pricelimit=plimit,
                     exectype=exectype, valid=valid, tradeid=tradeid)

    order.addinfo(**kwargs)

    vcorder = self._makeorder(order.ordtype, owner, data, size, price,
                              plimit, exectype, valid, tradeid,
                              **kwargs)

    return self.submit(order, vcorder)

这里有一个很关键的逻辑,就是一次我们会提交N个订单,有买有卖,那么第二次订单的成交顺序是什么呢?

如果按时间而言,卖单没有成交,买单是不够现金的,会产生margin的错误单(保证金不够)。

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

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

相关推荐

发表回复

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