BackTrader 中文文档(九)

原文:www.backtrader.com/

期货和现货补偿

原文:www.backtrader.com/docu/order-creation-execution/futurespot/future-vs-spot/

发布1.9.32.116添加了对在社区中提出的一个有趣用例的支持。

  • 通过未来开始交易,其中包括实物交割

  • 让指标告诉你些什么。

  • 如果需要,通过对现货价格进行操作来关闭头寸,有效地取消物理交割,无论是为了收到货物还是为了交付货物(并有望获利)。

    未来到期日与现货价格操作在同一天发生。

这意味着:

  • 平台从两种不同的资产中获取数据点。

  • 平台必须以某种方式理解资产之间的关系,并且操作现货价格将关闭在期货上开放的头寸。

    实际上,未来并没有关闭,只有物理交割是补偿的。

使用补偿概念,backtrader添加了一种让用户向平台传达在一个数据源上的事情将对另一个数据源产生补偿效果的方法。使用模式

import backtrader as bt

cerebro = bt.Cerebro()

data0 = bt.feeds.MyFavouriteDataFeed(dataname='futurename')
cerebro.adddata(data0)

data1 = bt.feeds.MyFavouriteDataFeed(dataname='spotname')
data1.compensate(data0)  # let the system know ops on data1 affect data0
cerebro.adddata(data1)

...

cerebro.run()

将所有内容放在一起

例子总是值得一千篇文章,所以让我们把所有的东西放在一起。

  • 使用backtrader源中的标准示例数据之一。这将是未来的。

  • 通过重复使用相同的数据源并添加一个过滤器,该过滤器将随机将价格移动到上/下几个点,以创建一个价差,来模拟类似但不同的价格。如下所示:

    # The filter which changes the close price
    def close_changer(data, *args, **kwargs):
        data.close[0] += 50.0 * random.randint(-1, 1)
        return False  # length of stream is unchanged` 
    
  • 将绘图到相同的轴上会混合默认包含的BuyObserver标记,因此标准观察器将被禁用,并手动重新添加以使用不同的每个数据标记进行绘图。

  • 位置将随机输入,并在 10 天后退出。

    这与期货到期期限不符,但这只是为了实现功能,而不是检查交易日历。

!!! 注意

 A simulation including execution on the spot price on the day of
  future expiration would require activating `cheat-on-close` to
  make sure the orders are executed when the future expires. This is
  not needed in this sample, because the expiration is being chosen
  at random.
  • 注意策略

    • buy操作在data0上执行。

    • sell操作在data1上执行。

    class St(bt.Strategy):
        def __init__(self):
            bt.obs.BuySell(self.data0, barplot=True)  # done here for
            BuySellArrows(self.data1, barplot=True)  # different markers per data
    
        def next(self):
            if not self.position:
                if random.randint(0, 1):
                    self.buy(data=self.data0)
                    self.entered = len(self)
    
            else:  # in the market
                if (len(self) - self.entered) >= 10:
                    self.sell(data=self.data1)` 
    

执行:

$ ./future-spot.py --no-comp

使用此图形输出。

image

它是有效的:

  • buy操作以指向上的绿色三角形的形式发出信号,图例告诉我们它们属于data0,如预期的那样。

  • sell操作以向下箭头的形式发出信号,图例告诉我们它们属于data1,如预期的那样。

  • 交易正在关闭,即使它们是以data0开仓并以data1平仓,也能实现期望的效果(在现实生活中,这意味着避免通过期货获得的商品的实物交割)。

如果没有补偿,人们可能会想象会发生什么。让我们来做一下:

$ ./future-spot.py --no-comp

输出结果

image

很明显,这是失败的:

  • 逻辑期望data0上的仓位在data1上的操作关闭,并且只有在市场中没有仓位时才开放data0上的仓位。

  • 补偿已被停用,并且对data0的初始操作(绿色三角形)从未关闭,因此不会启动任何其他操作,而data1上的空头仓位开始累积。

示例用法

$ ./future-spot.py --help
usage: future-spot.py [-h] [--no-comp]

Compensation example

optional arguments:
  -h, --help  show this help message and exit
  --no-comp

示例代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import random
import backtrader as bt

# The filter which changes the close price
def close_changer(data, *args, **kwargs):
    data.close[0] += 50.0 * random.randint(-1, 1)
    return False  # length of stream is unchanged

# override the standard markers
class BuySellArrows(bt.observers.BuySell):
    plotlines = dict(buy=dict(marker='$\u21E7$', markersize=12.0),
                     sell=dict(marker='$\u21E9$', markersize=12.0))

class St(bt.Strategy):
    def __init__(self):
        bt.obs.BuySell(self.data0, barplot=True)  # done here for
        BuySellArrows(self.data1, barplot=True)  # different markers per data

    def next(self):
        if not self.position:
            if random.randint(0, 1):
                self.buy(data=self.data0)
                self.entered = len(self)

        else:  # in the market
            if (len(self) - self.entered) >= 10:
                self.sell(data=self.data1)

def runstrat(args=None):
    args = parse_args(args)
    cerebro = bt.Cerebro()

    dataname = '../../datas/2006-day-001.txt'  # data feed

    data0 = bt.feeds.BacktraderCSVData(dataname=dataname, name='data0')
    cerebro.adddata(data0)

    data1 = bt.feeds.BacktraderCSVData(dataname=dataname, name='data1')
    data1.addfilter(close_changer)
    if not args.no_comp:
        data1.compensate(data0)
    data1.plotinfo.plotmaster = data0
    cerebro.adddata(data1)

    cerebro.addstrategy(St)  # sample strategy

    cerebro.addobserver(bt.obs.Broker)  # removed below with stdstats=False
    cerebro.addobserver(bt.obs.Trades)  # removed below with stdstats=False

    cerebro.broker.set_coc(True)
    cerebro.run(stdstats=False)  # execute
    cerebro.plot(volume=False)  # and plot

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=('Compensation example'))

    parser.add_argument('--no-comp', required=False, action='store_true')
    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()

StopTrail(Limit)

原文:www.backtrader.com/docu/order-creation-execution/trail/stoptrail/

版本1.9.35.116StopTrailStopTrailLimit订单执行类型添加到回测武器库。

注意

这仅在回测中实现,尚未针对实时经纪人实现

注意

用版本1.9.36.116更新。交互式经纪人支持StopTrailStopTrailLimitOCO

  • OCO总是将第 1 个订单作为参数oco指定为一组

  • StopTrailLimit:经纪人模拟和IB经纪人具有相同的行为。指定:price作为初始触发价格(也指定trailamount),然后plimi作为初始限价。两者之间的差异将确定limitoffset(限价与停止触发价格之间的距离)

用法模式完全集成到策略实例的标准buysellclose市场操作方法中。注意:

  • 指示希望的执行类型,如exectype=bt.Order.StopTrail

  • 以及是否必须使用固定距离或百分比距离计算跟踪价格

    • 固定距离:trailamount=10

    • 百分比距离:trailpercent=0.02(即:2%

如果通过发出buy进入市场,这是sellStopTrailtrailamount一起的操作:

  • 如果未指定price,则使用最新的close价格

  • trailamount从价格中减去以找到stop(或触发)价格

  • 下一个迭代的经纪人检查触发价格是否已达到

    • 如果:订单以Market执行类型的方法执行

    • 如果,则使用最新的close价格减去trailamount距离重新计算stop价格

    • 如果新价格上涨,则更新

    • 如果新价格将下跌(或根本不变),则将其丢弃

也就是说:跟踪止损价格随价格上涨而上涨,但如果价格开始下跌,则保持不变,以潜在地获利。

如果通过sell进入市场,然后通过StopTrail发出buy订单只是做相反的操作,即:价格向下跟随。

一些用法模式

# For a StopTrail going downwards
# last price will be used as reference
self.buy(size=1, exectype=bt.Order.StopTrail, trailamount=0.25)
# or
self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25)

# For a StopTrail going upwards
# last price will be used as reference
self.sell(size=1, exectype=bt.Order.StopTrail, trailamount=0.25)
# or
self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailamount=0.25)

也可以指定trailpercent而不是trailamount,价格与价格的距离将被计算为价格的百分比

# For a StopTrail going downwards with 2% distance
# last price will be used as reference
self.buy(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02)
# or
self.buy(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.0.02)

# For a StopTrail going upwards with 2% difference
# last price will be used as reference
self.sell(size=1, exectype=bt.Order.StopTrail, trailpercent=0.02)
# or
self.sell(size=1, exectype=bt.Order.StopTrail, price=10.50, trailpercent=0.02)

对于StopTrailLimit

  • 唯一的区别在于当触发跟踪止损价格时会发生什么。

  • 在这种情况下,订单以Limit订单的形式执行(与StopLimit订单的行为相同,但在这种情况下,触发价格是动态的)

    注意:必须指定plimit=x.xbuysell,这将是限价

    注意限价不像停止/触发价格那样动态更改

例如总是值得一看,因此通常的backtrader示例,其中

  • 使用移动平均线上穿进入市场多头

  • 使用跟踪止损退出市场

使用50点固定价格距离的执行

$ ./trail.py --plot --strat trailamount=50.0

这产生了以下图表

image

和以下输出:

**************************************************
2005-02-14,3075.76,3025.76,3025.76
----------
2005-02-15,3086.95,3036.95,3036.95
2005-02-16,3068.55,3036.95,3018.55
2005-02-17,3067.34,3036.95,3017.34
2005-02-18,3072.04,3036.95,3022.04
2005-02-21,3063.64,3036.95,3013.64
...
...
**************************************************
2005-05-19,3051.79,3001.79,3001.79
----------
2005-05-20,3050.45,3001.79,3000.45
2005-05-23,3070.98,3020.98,3020.98
2005-05-24,3066.55,3020.98,3016.55
2005-05-25,3059.84,3020.98,3009.84
2005-05-26,3086.08,3036.08,3036.08
2005-05-27,3084.0,3036.08,3034.0
2005-05-30,3096.54,3046.54,3046.54
2005-05-31,3076.75,3046.54,3026.75
2005-06-01,3125.88,3075.88,3075.88
2005-06-02,3131.03,3081.03,3081.03
2005-06-03,3114.27,3081.03,3064.27
2005-06-06,3099.2,3081.03,3049.2
2005-06-07,3134.82,3084.82,3084.82
2005-06-08,3125.59,3084.82,3075.59
2005-06-09,3122.93,3084.82,3072.93
2005-06-10,3143.85,3093.85,3093.85
2005-06-13,3159.83,3109.83,3109.83
2005-06-14,3162.86,3112.86,3112.86
2005-06-15,3147.55,3112.86,3097.55
2005-06-16,3160.09,3112.86,3110.09
2005-06-17,3178.48,3128.48,3128.48
2005-06-20,3162.14,3128.48,3112.14
2005-06-21,3179.62,3129.62,3129.62
2005-06-22,3182.08,3132.08,3132.08
2005-06-23,3190.8,3140.8,3140.8
2005-06-24,3161.0,3140.8,3111.0
...
...
...
**************************************************
2006-12-19,4100.48,4050.48,4050.48
----------
2006-12-20,4118.54,4068.54,4068.54
2006-12-21,4112.1,4068.54,4062.1
2006-12-22,4073.5,4068.54,4023.5
2006-12-27,4134.86,4084.86,4084.86
2006-12-28,4130.66,4084.86,4080.66
2006-12-29,4119.94,4084.86,4069.94

而不是等待通常的下穿模式,系统使用跟踪止损退出市场。例如,让我们看看第一次操作。

  • 进入多头时的收盘价:3075.76

  • 系统计算的跟踪止损价:3025.76(相距50个单位)

  • 样本计算的跟踪止损价:3025.76(每行显示的最后价格)

在第一次计算之后:

  • 收盘价上涨至3086.95,止损价调整为3036.95

  • 以下收盘价不超过3086.95,触发价格不变

在其他两次操作中也可以看到相同的模式。

为了比较,仅使用30点固定距离的执行(仅图表)

$ ./trail.py --plot --strat trailamount=30.0

和图表

image

最后一次执行后跟随trailpercent=0.02

$ ./trail.py --plot --strat trailpercent=0.02

相应的图表。

image

示例用法

$ ./trail.py --help
usage: trail.py [-h] [--data0 DATA0] [--fromdate FROMDATE] [--todate TODATE]
                [--cerebro kwargs] [--broker kwargs] [--sizer kwargs]
                [--strat kwargs] [--plot [kwargs]]

StopTrail Sample

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE  Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --todate TODATE      Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --cerebro kwargs     kwargs in key=value format (default: )
  --broker kwargs      kwargs in key=value format (default: )
  --sizer kwargs       kwargs in key=value format (default: )
  --strat kwargs       kwargs in key=value format (default: )
  --plot [kwargs]      kwargs in key=value format (default: )

示例代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        ma=bt.ind.SMA,
        p1=10,
        p2=30,
        stoptype=bt.Order.StopTrail,
        trailamount=0.0,
        trailpercent=0.0,
    )

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.crup = bt.ind.CrossUp(ma1, ma2)
        self.order = None

    def next(self):
        if not self.position:
            if self.crup:
                o = self.buy()
                self.order = None
                print('*' * 50)

        elif self.order is None:
            self.order = self.sell(exectype=self.p.stoptype,
                                   trailamount=self.p.trailamount,
                                   trailpercent=self.p.trailpercent)

            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )
            print('-' * 10)
        else:
            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )

def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'StopTrail Sample'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()

经纪人

经纪人

原文:www.backtrader.com/docu/broker/

参考

backtrader.brokers.BackBroker()

经纪人模拟器

该模拟支持不同的订单类型,检查提交订单的现金需求与当前现金的情况,跟踪每次cerebro迭代的现金和价值,并在不同的数据上保持当前位置。

cash根据仪器进行每次迭代调整,如期货

which a price change implies in real brokers the addition/substracion of
cash.

支持的订单类型:

  • Market:在下一个柱子的第 1^(st)个 tick(即open价格)执行

  • Close:用于一日内,在该订单以会话最后一个柱的收盘价执行

  • Limit:在会话期间看到给定的限价时执行

  • Stop:如果看到给定的停止价格,则执行Market订单

  • StopLimit:如果看到给定的停止价格,则启动Limit订单

因为经纪人是由Cerebro实例化的,而且应该(大多数情况下)没有理由替换经纪人,所以实例的参数不受用户控制。要更改此参数,有两种选择:

  1. 手动创建此类的实例,并使用cerebro.broker = instance将实例设置为run执行的经纪人

  2. 使用set_xxx通过cerebro.broker.set_xxx来设置值,其中\xxx`代表要设置的参数的名称

注意

cerebro.brokerCerebrogetbrokersetbroker方法支持的property

参数:

  • cash(默认值:10000):起始现金

  • commission(默认值:CommInfoBase(percabs=True))适用于所有资产的基础佣金方案

  • checksubmit(默认值:True):在将订单接受到系统之前检查保证金/现金

  • eosbar(默认值:False):对于一日内柱,如果某个柱具有与会话结束相同的time,则将其视为会话结束。这通常不是情况,因为一些柱(最终拍卖)会在会话结束后几分钟内由许多交易所为许多产品产生

  • eosbar(默认值:False):对于一日内柱,如果某个柱具有与会话结束相同的time,则将其视为会话结束。这通常不是情况,因为一些柱(最终拍卖)会在会话结束后几分钟内由许多交易所为许多产品产生

  • filler(默认值:None

    一个带有签名的可调用函数:callable(order, price, ago)

    • order:显然是执行的订单。这提供了对数据(以及ohlcvolume值)、执行类型、剩余大小(order.executed.remsize)和其他内容的访问。

      请检查Order文档和参考,以了解Order实例中可用的内容

    • price:订单将在ago柱中执行的价格

    • ago:用于与order.data一起使用以提取ohlcvolume价格的索引。在大多数情况下,这将是0,但对于Close订单的一个特殊情况,这将是-1

      为了获得柱形体积(例如):volume = order.data.voluume[ago]

    可调用函数必须返回执行大小(大于等于 0 的值)

    可调用函数当然可以是一个与前述签名匹配的对象__call__

    默认值为None时,订单将在一次性完全执行

  • slip_perc(默认值:0.0)应该使用的百分比绝对值(和正数)使买入/卖出订单的价格上涨/下跌

    注意:

    • 0.011%

    • 0.0010.1%

  • slip_fixed(默认值:0.0)应使用的以单位(和正数)表示的百分比来使价格上涨/下跌以进行买入/卖出订单滑点

    注意:如果slip_perc不为零,则它优先于此。

  • slip_open(默认值:False)是否为订单执行滑点价格,其将特定使用下一个条的开盘价格。一个例子是使用下一个可用的刻度执行的Market订单,即:该条的开盘价。

    这也适用于一些其他执行,因为逻辑试图检测开盘价格是否会与移动到新条时请求的价格/执行类型匹配。

  • slip_match(默认值:True

    如果设置为True,则经纪人将通过将滑点限制在高/低价格上来提供匹配,以防超出。

    如果为False,则经纪人不会以当前价格匹配订单,并将尝试在下一次迭代期间执行

  • slip_limit(默认值:True

    Limit订单,给定所请求的确切匹配价格,即使slip_matchFalse,也将匹配。

    此选项控制该行为。

    如果为True,则Limit订单将通过将价格限制在限价/高/低价格上来匹配

    如果False且滑点超过上限,则不会匹配

  • slip_out(默认值:False

    即使价格跌出-范围,也要提供滑点

  • coc(默认值:False

    Cheat-On-Close 将其设置为True并使用set_coc启用

    matching a `Market` order to the closing price of the bar in which
    the order was issued. This is actually *cheating*, because the bar
    is *closed* and any order should first be matched against the prices
    in the next bar` 
    
  • coo(默认值:False

    Cheat-On-Open 将其设置为True并使用set_coo启用

    matching a `Market` order to the opening price, by for example
    using a timer with `cheat` set to `True`, because such a timer
    gets executed before the broker has evaluated` 
    
  • int2pnl(默认值:True

    将生成的利息(如果有)分配给减少持仓的操作的损益。可能存在这种情况:不同的策略正在竞争,利息将以不确定的方式分配给其中任何一个。

  • shortcash(默认值:True

    如果为 True,则在对类似股票的资产进行空头操作时会增加现金,并且计算出的资产值将为负。

    如果为False,则现金将被扣除为操作成本,并且计算出的值将为正,以保持相同数量

  • fundstartval(默认值:100.0

    此参数控制以类似基金的方式测量绩效的起始值,即:可以添加和扣除现金以增加股份数量。绩效不是使用投资组合的净资产值来衡量的,而是使用基金的价值来衡量。

  • fundmode(默认值:False

    如果设置为 True,分析器如 TimeReturn 可以根据基金价值而不是总净资产自动计算回报率

set_cash(cash)

设置 cash 参数(别名:setcash

get_cash()

返回当前现金(别名:getcash

get_value(datas=None, mkt=False, lever=False)

返回给定数据的投资组合价值(如果数据为 None,则将返回总投资组合价值)(别名:getvalue

set_eosbar(eosbar)

设置 eosbar 参数(别名:seteosbar

set_checksubmit(checksubmit)

设置 checksubmit 参数

set_filler(filler)

为成交量填充执行设置一个成交量填充器

set_coc(coc)

配置“在订单栏上购买收盘价”的“Cheat-On-Close”方法

set_coo(coo)

配置“在订单栏上购买开盘价”的“Cheat-On-Open”方法

set_int2pnl(int2pnl)

配置利息分配给损益

set_fundstartval(fundstartval)

设置基金样式表现追踪器的起始值

set_slippage_perc(perc, slip_open=True, slip_limit=True, slip_match=True, slip_out=False)

配置滑点以百分比为基础

set_slippage_fixed(fixed, slip_open=True, slip_limit=True, slip_match=True, slip_out=False)

配置滑点以基于固定点进行

get_orders_open(safe=False)

返回仍然打开的订单的可迭代对象(未执行或部分执行)

返回的订单不得被更改。

如果需要订单操作,请将参数 safe 设置为 True

getcommissioninfo(data)

检索与给定 data 相关联的 CommissionInfo 方案

setcommission(commission=0.0, margin=None, mult=1.0, commtype=None, percabs=True, stocklike=False, interest=0.0, interest_long=False, leverage=1.0, automargin=False, name=None)

此方法使用参数为经纪人管理的资产设置 CommissionInfo 对象。请查阅 CommInfoBase 的参考资料

如果名称为 None,则对于找不到其他 CommissionInfo 方案的资产,这将是默认值

addcommissioninfo(comminfo, name=None)

如果 nameNone,则添加一个 CommissionInfo 对象,如果 nameNone,则为所有资产设置默认值

getposition(data)

返回给定 data 的当前持仓状态(Position 实例)

get_fundshares()

返回基金模式中当前股份数量

get_fundvalue()

返回基金样式股票价值

add_cash(cash)

向系统添加/移除现金(使用负值来移除)

滑点

原文:www.backtrader.com/docu/slippage/slippage/

回测无法保证真实市场条件。无论市场模拟有多好,在真实市场条件下都可能发生滑点。这意味着:

  • 请求的价格可能无法匹配。

集成的回测经纪人支持滑点。以下参数可以传递给经纪人

  • slip_perc (默认值: 0.0) 绝对百分比(正数),用于上下滑动买入/卖出订单的价格

    注意:

    • 0.011%

    • 0.0010.1%

  • slip_fixed (默认值: 0.0) 单位百分比(正数),用于上下滑动买入/卖出订单的价格

    注意:如果slip_perc不为零,则它优先于此。

  • slip_open (默认值: False) 是否为订单执行滑动价格,该价格特别使用下一个柱的开盘价格。一个示例是使用下一个可用刻度执行的Market订单,即柱的开盘价格。

    这也适用于其他一些执行,因为逻辑试图检测开盘价格是否会在移动到新柱时匹配请求的价格/执行类型。

  • slip_match (默认值: True)

    如果为True,经纪人将通过在超出时将滑点限制在high/low价格上来提供匹配。

    如果 False,经纪人将不会根据当前价格匹配订单,并将在下一次迭代中尝试执行

  • slip_limit (默认值: True)

    即使 slip_matchFalse,给定请求的精确匹配价格的Limit订单也会被匹配。

    此选项控制该行为。

    如果为True,则Limit订单将通过将价格限制在limit / high/low价格上来匹配

    如果 False 并且滑点超过上限,则不会匹配

  • slip_out (默认值: False)

    即使价格超出high - low范围,也提供滑点

工作原理

为了决定何时应用滑点,订单执行类型被考虑在内:

  • Close - 不应用滑点

    此订单与close价格匹配,此价格是当天的最后一个价格。滑点不会发生,因为订单只能在会话的最后一个刻度发生,这是一个唯一的价格,没有容忍度。

  • Market - 滑点被应用

    请检查slip_open异常。因为Market订单将与下一个柱的开盘价格匹配。

  • Limit - 滑点按照这个逻辑应用

    • 如果匹配价格将是开盘价格,则根据参数 slip_open 应用滑点。如果应用,则价格永远不会比请求的limit价格更差

    • 如果匹配价格不是limit价格,则应用滑点,并在high/low上限制。在这种情况下,slip_mlimit适用于决定是否在超出上限时会发生匹配

    • 如果匹配价格是limit价格,则不会应用滑点

  • Stop - 一旦订单被触发,与市价订单相同的逻辑将适用

  • StopLimit - 一旦订单被触发,与限价订单相同的逻辑将适用

该方法试图在模拟和可用数据的限制内提供尽可能逼真的方法

配置滑点

经纪人已经由每个运行的智能交易引擎实例化,具有默认参数。有两种方法可以更改其行为:

实际示例

源代码包含一个示例,该示例使用了执行类型为市价的订单以及使用信号多头/空头方法。这应该能帮助理解其逻辑。

一个没有滑点并且具有初始图表供以后参考的运行:

$ ./slippage.py --plot
01 2005-03-22 23:59:59 SELL Size: -1 / Price: 3040.55
02 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3088.47
03 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3088.47
04 2005-04-19 23:59:59 SELL Size: -1 / Price: 2948.38
05 2005-04-19 23:59:59 SELL Size: -1 / Price: 2948.38
06 2005-05-19 23:59:59 BUY  Size: +1 / Price: 3034.88
...
35 2006-12-19 23:59:59 BUY  Size: +1 / Price: 4121.01

图片

同样的运行使用了配置为1.5%滑点

$ ./slippage.py --slip_perc 0.015
01 2005-03-22 23:59:59 SELL Size: -1 / Price: 3040.55
02 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3088.47
03 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3088.47
04 2005-04-19 23:59:59 SELL Size: -1 / Price: 2948.38
05 2005-04-19 23:59:59 SELL Size: -1 / Price: 2948.38
06 2005-05-19 23:59:59 BUY  Size: +1 / Price: 3034.88
...
35 2006-12-19 23:59:59 BUY  Size: +1 / Price: 4121.01

没有变动。这是该场景的预期行为。

  • 执行类型:市价

  • slip_open没有被设置为True

    市价订单与下一根柱的开盘价格匹配,我们不允许open价格被移动。

slip_open设置为True的一个运行设置:

$ ./slippage.py --slip_perc 0.015 --slip_open
01 2005-03-22 23:59:59 SELL Size: -1 / Price: 3021.66
02 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3088.47
03 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3088.47
04 2005-04-19 23:59:59 SELL Size: -1 / Price: 2948.38
05 2005-04-19 23:59:59 SELL Size: -1 / Price: 2948.38
06 2005-05-19 23:59:59 BUY  Size: +1 / Price: 3055.14
...
35 2006-12-19 23:59:59 BUY  Size: +1 / Price: 4121.01

人们可以立即看到价格已经变动。并且分配的价格比操作 35 的最坏或相等。这不是复制粘贴错误

  • 2016-12-19 的openhigh是相同的。

    价格不能被推高到high之上,因为那将意味着返回一个不存在的价格。

当然,backtrader允许匹配超出high-low范围的价格,如果愿意的话,可以激活slip_out进行运行:

$ ./slippage.py --slip_perc 0.015 --slip_open --slip_out
01 2005-03-22 23:59:59 SELL Size: -1 / Price: 2994.94
02 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3134.80
03 2005-04-11 23:59:59 BUY  Size: +1 / Price: 3134.80
04 2005-04-19 23:59:59 SELL Size: -1 / Price: 2904.15
05 2005-04-19 23:59:59 SELL Size: -1 / Price: 2904.15
06 2005-05-19 23:59:59 BUY  Size: +1 / Price: 3080.40
...
35 2006-12-19 23:59:59 BUY  Size: +1 / Price: 4182.83

用于匹配的匹配表达式将是:哦天呐!(我的天啊!)。价格明显超出了范围。只需看看操作 35,该操作在4182.83处匹配。快速检查本文档中的图表可以看到该资产从未接近过该价格。

slip_match的默认值为True,这意味着backtrader提供了一个匹配,无论是带上限价还是不带上限价,如上所示。让我们将其禁用:

$ ./slippage.py --slip_perc 0.015 --slip_open --no-slip_match
01 2005-04-15 23:59:59 SELL Size: -1 / Price: 3028.10
02 2005-05-18 23:59:59 BUY  Size: +1 / Price: 3029.40
03 2005-06-01 23:59:59 BUY  Size: +1 / Price: 3124.03
04 2005-10-06 23:59:59 SELL Size: -1 / Price: 3365.57
05 2005-10-06 23:59:59 SELL Size: -1 / Price: 3365.57
06 2005-12-01 23:59:59 BUY  Size: +1 / Price: 3499.95
07 2005-12-01 23:59:59 BUY  Size: +1 / Price: 3499.95
08 2006-02-28 23:59:59 SELL Size: -1 / Price: 3782.71
09 2006-02-28 23:59:59 SELL Size: -1 / Price: 3782.71
10 2006-05-23 23:59:59 BUY  Size: +1 / Price: 3594.68
11 2006-05-23 23:59:59 BUY  Size: +1 / Price: 3594.68
12 2006-11-27 23:59:59 SELL Size: -1 / Price: 3984.37
13 2006-11-27 23:59:59 SELL Size: -1 / Price: 3984.37

天啊!从 35 降到了 13。原因是:

停用slip_match将阻止匹配操作,如果滑点将匹配价格推高到high之上或推低到柱的low之下。看起来,随着请求的滑点1.5%,大约有 22 个操作无法执行。

这些示例应该已经展示了不同的滑点选项是如何共同工作的。

以开盘作弊

原文:www.backtrader.com/docu/cerebro/cheat-on-open/cheat-on-open/

版本 1.9.44.116 增加了对 Cheat-On-Open 的支持。这似乎是对那些全情投入的人的一项需求功能,他们在柱结束后做了计算,但期望与open价格匹配。

开盘价格出现差距(向上或向下,取决于buysell是否生效)并且现金不足以进行全情投入操作时,此用例会失败。这迫使经纪人拒绝操作。

虽然人们可以尝试用积极的[1]索引方法来展望未来,但这需要预加载数据,而这并不总是可用的。

模式:

cerebro = bt.Cerebro(cheat_on_open=True)

这个:

  • 在系统中激活了额外的周期,该周期调用策略中的next_opennextstart_openprenext_open方法。

    决定增加一个额外的方法系列是为了清晰地区分常规方法和作弊模式之间的区别。常规方法是基于所检查的价格不再可用且未来未知的基础上运行的,而作弊模式则是另一种操作。

    这也避免了对常规next方法进行两次调用。

当处于xxx_open方法内部时,以下情况成立:

  • 指标尚未重新计算,并保留了上一个周期中在等效的xxx常规方法中最后看到的值。

  • 经纪人尚未评估新周期的待处理订单,可以引入新订单,如果可能的话将进行评估。

请注意:

  • Cerebro还有一个名为broker_coo(默认为True)的参数,告诉cerebro如果已经激活了cheat-on-open,它应该尽可能地在经纪人中也激活它。

    仿真经纪人有一个名为:coo的参数和一个设置它的方法名为set_coo

尝试作弊开盘

下面的示例具有具有 2 种不同行为的策略:

  • 如果cheat-on-openTrue,它将只从next_open操作

  • 如果cheat-on-openFalse,它将只从next操作

在这两种情况下,匹配价格必须是相同的

  • 如果不作弊,订单将在前一天结束时发布,并将与下一个输入价格(即open价格)匹配

  • 如果作弊,订单将在执行当天发布。因为订单是在经纪人评估订单之前发布的,所以它也将与下一个输入价格(即open价格)匹配。

    第二种情况允许计算全情投入策略的确切赌注,因为可以直接访问当前的open价格。

在这两种情况下

  • 当前的openclose价格将从next打印出来。

常规执行:

$ ./cheat-on-open.py --cerebro cheat_on_open=False

...
2005-04-07 next, open 3073.4 close 3090.72
2005-04-08 next, open 3092.07 close 3088.92
Strat Len 68 2005-04-08 Send Buy, fromopen False, close 3088.92
2005-04-11 Buy Executed at price 3088.47
2005-04-11 next, open 3088.47 close 3080.6
2005-04-12 next, open 3080.42 close 3065.18
...

图片

顺序:

  • 在 2005-04-08close之后发布

  • 它是在 2005-04-11 执行的,以3088.47open价格执行。

作弊执行:

$ ./cheat-on-open.py --cerebro cheat_on_open=True

...
2005-04-07 next, open 3073.4 close 3090.72
2005-04-08 next, open 3092.07 close 3088.92
2005-04-11 Send Buy, fromopen True, close 3080.6
2005-04-11 Buy Executed at price 3088.47
2005-04-11 next, open 3088.47 close 3080.6
2005-04-12 next, open 3080.42 close 3065.18
...

图片

顺序:

  • 在 2005-04-11 开盘 之前发布

  • 它在 2005-04-11 执行,开盘价为3088.47

而图表上整体的结果也是相同的。

结论

在开盘前作弊允许在开盘前发布订单,例如允许精确计算全仓情况下的股份。

样本用法

$ ./cheat-on-open.py --help
usage: cheat-on-open.py [-h] [--data0 DATA0] [--fromdate FROMDATE]
                        [--todate TODATE] [--cerebro kwargs] [--broker kwargs]
                        [--sizer kwargs] [--strat kwargs] [--plot [kwargs]]

Cheat-On-Open Sample

optional arguments:
  -h, --help           show this help message and exit
  --data0 DATA0        Data to read in (default:
                       ../../datas/2005-2006-day-001.txt)
  --fromdate FROMDATE  Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --todate TODATE      Date[time] in YYYY-MM-DD[THH:MM:SS] format (default: )
  --cerebro kwargs     kwargs in key=value format (default: )
  --broker kwargs      kwargs in key=value format (default: )
  --sizer kwargs       kwargs in key=value format (default: )
  --strat kwargs       kwargs in key=value format (default: )
  --plot [kwargs]      kwargs in key=value format (default: )

样本来源

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        periods=[10, 30],
        matype=bt.ind.SMA,
    )

    def __init__(self):
        self.cheating = self.cerebro.p.cheat_on_open
        mas = [self.p.matype(period=x) for x in self.p.periods]
        self.signal = bt.ind.CrossOver(*mas)
        self.order = None

    def notify_order(self, order):
        if order.status != order.Completed:
            return

        self.order = None
        print('{}  {} Executed at price {}'.format(
            bt.num2date(order.executed.dt).date(),
            'Buy' * order.isbuy() or 'Sell', order.executed.price)
        )

    def operate(self, fromopen):
        if self.order is not None:
            return
        if self.position:
            if self.signal < 0:
                self.order = self.close()
        elif self.signal > 0:
            print('{} Send Buy, fromopen {}, close {}'.format(
                self.data.datetime.date(),
                fromopen, self.data.close[0])
            )
            self.order = self.buy()

    def next(self):
        print('{} next, open {} close {}'.format(
            self.data.datetime.date(),
            self.data.open[0], self.data.close[0])
        )

        if self.cheating:
            return
        self.operate(fromopen=False)

    def next_open(self):
        if not self.cheating:
            return
        self.operate(fromopen=True)

def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'Cheat-On-Open Sample'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()

Fillers

原文:www.backtrader.com/docu/filler/

当涉及使用成交量执行订单时,backtrader 经纪人模拟具有默认策略:

  • 忽略成交量

这基于两个前提:

  • 在足够流动的市场中进行交易,以完全吸收一次性 买入/卖出 订单

  • 真实成交量匹配需要真实世界

    快速示例是 Fill or Kill 订单。即使到了 tick 分辨率并且有足够的 fill 量,backtrader 经纪人也无法知道市场上有多少额外的参与者,以区分这样一个订单是否会匹配以坚持 Fill 部分,或者该订单是否应该 Kill

但是 broker 可以接受 Volume Fillers,这些填充器确定在给定时间点使用多少成交量用于 order matching

填充器的签名

backtrader 生态系统中的一个 filler 可以是符合以下签名的任何 callable

callable(order, price, ago)

其中:

  • order 是将要执行的订单

    此对象提供对 data 对象的访问,该对象是操作目标,创建大小/价格,执行价格/大小/剩余大小和其他细节

  • 订单将被执行的 price

  • ago 是在其中查找体积和价格元素的 order 中的 data 的索引

    在几乎所有情况下,这将是 0(当前时间点),但在一种角落情况下,以涵盖 Close 订单,这可能是 -1

    例如,要访问条形图的体积:

    barvolume = order.data.volume[ago]` 
    

可调用对象可以是函数,也可以是例如支持 __call__ 方法的类的实例,如:

class MyFiller(object):
    def __call__(self, order, price, ago):
        pass

向经纪人添加一个 Filler

最直接的方法是使用 set_filler

import backtrader as bt

cerebro = Cerebro()
cerebro.broker.set_filler(bt.broker.fillers.FixedSize())

第二种选择是完全替换 broker,虽然这可能只适用于已重写部分功能的 BrokerBack 的子类:

import backtrader as bt

cerebro = Cerebro()
filler = bt.broker.fillers.FixedSize()
newbroker = bt.broker.BrokerBack(filler=filler)
cerebro.broker = newbroker

示例

backtrader 源代码中包含一个名为 volumefilling 的示例,允许测试一些集成的 fillers(最初全部)

参考资料

class backtrader.fillers.FixedSize()

使用 百分比 的体积返回给定订单的执行大小。

此百分比通过参数 perc 设置

参数:

  • size(默认值:None)要执行的最大尺寸。如果执行时的实际体积小于大小,则也是限制

    如果此参数的值计算为 False,则整个条的体积将用于匹配订单

class backtrader.fillers.FixedBarPerc()

使用条中体积的 百分比 返回给定订单的执行大小。

此百分比通过参数 perc 设置

参数:

  • perc(默认值:100.0)(有效值:0.0 - 100.0

    用于执行订单的成交量栏的百分比

class backtrader.fillers.BarPointPerc()

返回给定订单的执行大小。成交量将在 high-low 范围内均匀分布,使用 minmov 进行分区。

从给定价格的分配交易量中,将使用 perc 百分比

参数:

  • minmov(默认值:0.01

    最小价格变动。用于将范围 - 分割,以在可能的价格之间按比例分配交易量

  • perc(默认值:100.0)(有效值:0.0 - 100.0

    分配给订单执行价格的交易量百分比,用于匹配

位置

原文:www.backtrader.com/docu/position/

通常在策略内部检查资产的持仓情况:

  • position(一种属性)或 getposition(data=None, broker=None)

    这将返回由 cerebro 提供的默认 broker 中策略的 datas[0] 上的位置

一个持仓只是指:

  • 资产持有 size

  • 平均价格为 price

它作为一种状态,并且例如可以用来决定是否要发出订单(例如:仅在没有持仓时才进入多头持仓)

参考:Position

类 backtrader.position.Position(size=0, price=0.0)

保持并更新持仓的大小和价格。该对象与任何资产无关,仅保留大小和价格。

成员属性:

* size (int): current size of the position

* price (float): current price of the position

Position 实例可以使用 len(position) 进行测试,以查看大小是否为空

交易

原文:www.backtrader.com/docu/trade/

交易的定义:

  • 当仪器中的持仓从 0 变为大小 X 时,交易就开始了,大小可以是正的/负的,分别表示多头/空头仓位)

  • 当仓位从 X 变为 0 时,交易关闭。

以下两个动作:

  • 正至负

  • 负至正

实际上被视为:

  1. 一个交易已经关闭(仓位从 X 变为 0)

  2. 一个新交易已经打开(仓位从 0 变为 Y)

交易仅用于信息提供,没有用户可调用的方法。

参考:交易

类 backtrader.trade.Trade(data=None, tradeid=0, historyon=False, size=0, price=0.0, value=0.0, commission=0.0)

跟踪交易的生命周期:大小、价格、佣金(和价值?)

一个交易从 0 开始,可以增加和减少,并且如果回到 0 可以被视为关闭。

交易可以是多头(正大小)或空头(负大小)

一个交易不打算被反转(逻辑上不支持)

成员属性:

  • ref: 唯一的交易标识符

  • status (int): Created、Open、Closed 中的一个

  • tradeid: 在创建订单时传递给订单的交易 id 分组,默认为 0

  • size (int): 交易的当前大小

  • price (float): 交易的当前价格

  • value (float): 交易的当前价值

  • commission (float): 当前累积佣金

  • pnl (float): 交易的当前利润和损失(毛利润)

  • pnlcomm (float): 交易的当前利润和损失减去佣金(净利润)

  • isclosed (bool): 记录最后一个更新是否关闭了交易(将大小设置为 null)

  • isopen (bool): 记录是否有任何更新打开了交易

  • justopened (bool): 如果交易刚刚开启

  • baropen (int): 开启此交易的柱

  • dtopen (float): 交易开启的浮点编码日期时间

    • 使用方法 open_datetime 获取 Python datetime.datetime 或使用平台提供的 num2date 方法
  • barclose (int): 关闭此交易的柱

  • dtclose (float): 交易关闭的浮点编码日期时间

    • 使用方法 close_datetime 获取 Python datetime.datetime 或使用平台提供的 num2date 方法
  • barlen (int): 此交易持续的周期数

  • historyon (bool): 是否记录历史记录

  • history (list): 每个“update”事件更新的列表,包含更新后的状态和更新中使用的参数

    历史记录中的第一个条目是开仓事件,最后一个条目是关闭事件

佣金方案

佣金:股票与期货

原文:www.backtrader.com/docu/commission-schemes/commission-schemes/

不可知论

在继续之前,让我们记住,backtrader试图保持对数据表示的不可知性。 可以将不同的佣金方案应用于相同的数据集。

让我们看看如何做到这一点。

使用经纪商快捷方式

这使得最终用户远离了CommissionInfo对象,因为可以通过单个函数调用来创建/设置佣金方案。 在常规的cerebro创建/设置过程中,只需在broker成员属性上添加一个对setcommission的调用。 以下调用为使用Interactive Brokers时的Eurostoxx50期货设置了一个常规佣金方案:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)

由于大多数用户通常只会测试单个工具,因此这就是所有的内容。 如果您已为数据源(data feed)指定了name,因为在图表上同时考虑了多个工具,因此可以稍微扩展此调用,如下所示:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0, name='Eurostoxxx50')

在这种情况下,此即时佣金方案仅适用于名称匹配为Eurostoxx50的工具。

设置佣金参数的含义

  • commission(默认值:0.0

    绝对或百分比单位中每个操作的货币单位成本。

    在上面的示例中,每个buy合约的费用为 2.0 欧元,每个sell合约再次为 2.0 欧元。

    这里重要的问题是何时使用绝对值或百分比值。

    • 如果margin评估为False(例如为 False、0 或 None),则会认为commission表示price乘以size操作值的百分比

    • 如果margin是其他值,则认为操作发生在类似期货的工具上,commission是每个size合约的固定价格

  • margin(默认值:None

    使用期货类似工具时需要的保证金。 如上所述

    • 如果未设置**margin**,则将理解commission为以百分比表示,并应用于buysell操作的price * size组件。

    • 如果设置了margin,则将理解commission为固定值,该值将乘以buysell操作的size组件。

  • mult(默认值:1.0)

    对于类似期货的工具,这确定要应用于利润和损失计算的乘数。

    这就是期货同时具有吸引力和风险的原因。

  • name(默认值:无)

    将佣金方案的应用限制为与name匹配的工具

    这可以在数据源创建期间设置。

    如果未设置,该方案将适用于系统中的任何数据。

现在有两个示例:股票 vs 期货

上述期货示例:

cerebro.broker.setcommission(commission=2.0, margin=2000.0, mult=10.0)

以股票为例:

cerebro.broker.setcommission(commission=0.005)  # 0.5% of the operation value

注意

第 2 种语法不设置marginmultbacktrader会尝试通过考虑佣金为来采用智能方法。

要完全指定佣金方案,需要创建CommissionInfo的子类

创建永久委员会方案

可以通过直接使用CommissionInfo类来创建更永久的佣金方案。用户可以选择在某处定义此定义:

import backtrader as bt

commEurostoxx50 = bt.CommissionInfo(commission=2.0, margin=2000.0, mult=10.0)

稍后在另一个 Python 模块中应用它与 addcommissioninfo

from mycomm import commEurostoxx50

...

cerebro.broker.addcommissioninfo(commEuroStoxx50, name='Eurostoxxx50')

CommissionInfo是一个对象,它使用与backtrader环境中的其他对象相同的params声明。因此,以上内容也可以表示为:

import backtrader as bt

class CommEurostoxx50(bt.CommissionInfo):
    params = dict(commission=2.0, margin=2000.0, mult=10.0)

以后:

from mycomm import CommEurostoxx50

...

cerebro.broker.addcommissioninfoCommEuroStoxx50(), name='Eurostoxxx50')

现在进行与 SMA 交叉的“真实”比较

使用简单移动平均线交叉作为入/出信号,同一数据集将使用期货类似的佣金方案进行测试,然后使用类似于股票的方案进行测试。

注意

期货持仓不仅可以给出进入/退出行为,还可以在每次发生时进行反转行为。但这个示例是关于比较委员会方案的。

代码(请参阅底部的完整策略)是相同的,可以在定义策略之前选择方案。

futures_like = True

if futures_like:
    commission, margin, mult = 2.0, 2000.0, 10.0
else:
    commission, margin, mult = 0.005, None, 1

只需将futures_like设置为 false 即可运行类似于股票的方案。

已添加一些记录代码以评估不同佣金方案的影响。让我们只关注前两个操作。

对于期货:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00
2006-04-12, OPERATION PROFIT, GROSS 328.00, NET 324.00
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 2000.00, Comm 2.00
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 2000.00, Comm 2.00
2006-05-02, OPERATION PROFIT, GROSS -243.30, NET -247.30

对于股票:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3786.93, Comm 18.93
2006-04-12, OPERATION PROFIT, GROSS 32.80, NET -4.91
2006-04-20, BUY CREATE, 3860.00
2006-04-21, BUY EXECUTED, Price: 3863.57, Cost: 3863.57, Comm 19.32
2006-04-28, SELL CREATE, 3839.90
2006-05-02, SELL EXECUTED, Price: 3839.24, Cost: 3839.24, Comm 19.20
2006-05-02, OPERATION PROFIT, GROSS -24.33, NET -62.84

第一次操作的价格如下:

  • 买入(执行)-> 3754.13 / 卖出(执行)-> 3786.93

    • 期货利润与损失(含佣金):324.0

    • 股票利润与损失(含佣金):-4.91

嘿!!佣金完全吃掉了股票操作的任何利润,但对期货操作只是造成了小小的影响。

第二次操作:

  • 买入(执行)-> 3863.57 / 卖出(执行)-> 3389.24

    • 期货利润与损失(含佣金):-247.30

    • 股票利润与损失(含佣金):-62.84

对于这个负操作,期货的影响更大。

但:

  • 期货累积净利润与损失:324.00 + (-247.30) = 76.70

  • 股票累积净利润与损失:(-4.91) + (-62.84) = -67.75

累积效果可以在下面的图表中看到,在完整年份结束时,期货产生了更大的利润,但也遭受了更大的回撤(更深的亏损)

但重要的是:无论是期货还是股票…… 都可以进行回测。

期货佣金

image

股票佣金

image

代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind

futures_like = True

if futures_like:
    commission, margin, mult = 2.0, 2000.0, 10.0
else:
    commission, margin, mult = 0.005, None, 1

class SMACrossOver(bt.Strategy):
    def log(self, txt, dt=None):
  ''' Logging function fot this strategy'''
        dt = dt or self.datas[0].datetime.date(0)
        print('%s, %s' % (dt.isoformat(), txt))

    def notify(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enougth cash
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))

                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
                self.opsize = order.executed.size
            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

                gross_pnl = (order.executed.price - self.buyprice) * \
                    self.opsize

                if margin:
                    gross_pnl *= mult

                net_pnl = gross_pnl - self.buycomm - order.executed.comm
                self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
                         (gross_pnl, net_pnl))

    def __init__(self):
        sma = btind.SMA(self.data)
        # > 0 crossing up / < 0 crossing down
        self.buysell_sig = btind.CrossOver(self.data, sma)

    def next(self):
        if self.buysell_sig > 0:
            self.log('BUY CREATE, %.2f' % self.data.close[0])
            self.buy()  # keep order ref to avoid 2nd orders

        elif self.position and self.buysell_sig < 0:
            self.log('SELL CREATE, %.2f' % self.data.close[0])
            self.sell()

if __name__ == '__main__':
    # Create a cerebro entity
    cerebro = bt.Cerebro()

    # Add a strategy
    cerebro.addstrategy(SMACrossOver)

    # Create a Data Feed
    datapath = ('../../datas/2006-day-001.txt')
    data = bt.feeds.BacktraderCSVData(dataname=datapath)

    # Add the Data Feed to Cerebro
    cerebro.adddata(data)

    # set commission scheme -- CHANGE HERE TO PLAY
    cerebro.broker.setcommission(
        commission=commission, margin=margin, mult=mult)

    # Run over everything
    cerebro.run()

    # Plot the result
    cerebro.plot()

参考

类 backtrader.CommInfoBase()

基于委员会方案的基类。

参数:

  • commission(默认值:0.0):以百分比或货币单位表示的基础佣金值

  • mult(默认为1.0):应用于资产的值/利润的乘数

  • margin(默认值:None):需要开设/持有操作的货币单位金额。只有当类中的最终_stocklike属性设置为False时才适用

  • automargin(默认:False):方法get_margin使用的,用于自动计算以下策略所需的保证金/担保

    • 如果参数automargin的评估为False,则使用参数margin

    • 如果automargin < 0,则使用参数multmult * price

    • 如果automargin > 0,则使用参数automarginautomargin * price

  • commtype(默认:None):支持的值为CommInfoBase.COMM_PERC(将佣金理解为%)和CommInfoBase.COMM_FIXED(将佣金理解为货币单位)

    None的默认值是支持的值,以保持与传统的CommissionInfo对象的兼容性。 如果commtype设置为 None,则适用以下规则:

    • marginNone:内部_commtype设置为COMM_PERC_stocklike设置为True(与股票的百分比方式运作)

    • 如果margin不是None_commtype设置为COMM_FIXED_stocklike设置为False(使用期货的固定来回佣金)

    如果此参数设置为None之外的内容,则它将传递给内部的_commtype属性,并且参数stocklike和内部属性_stocklike也是如此

  • stocklike(默认:False):指示工具是否类似于股票或期货(参见上文的commtype讨论)

  • percabs(默认:False):当commtype设置为 COMM_PERC 时,参数commission是否必须理解为 XX%或 0.XX

    如果此参数为True:0.XX 如果此参数为False:XX%

  • interest(默认:0.0

    如果这不是零,则这是持有空头卖出头寸所收取的年息。 这主要是针对股票的空头卖出

    公式:days * price * abs(size) * (interest / 365)

    必须用绝对值指定:0.05 -> 5%

    注意

    通过覆盖方法_get_credit_interest可以更改行为

  • interest_long(默认:False

    某些产品,如 ETF,对空头和多头头寸收取利息。 如果这是True并且interest不为零,则将在两个方向上收取利息

  • leverage(默认:1.0

    对于所需现金的资产的杠杆倍数

- _stocklike()

用于类 Stock-like/Futures-like 行为的最终值

- _commtype()

PERC vs FIXED 佣金的最终值

这两个参数在内部使用,而不是声明的参数,以启用该方法
描述的兼容性检查适用于遗留的CommissionInfo()
对象()
类 backtrader.CommissionInfo()

实际佣金方案的基类。

CommInfoBase 被创建以保持对backtrader提供的原始,不完整的支持的支持。新的佣金方案派生自这个类,这些类是CommInfoBase的子类。

percabs的默认值也更改为True

参数:

  • percabs(默认:True):当commtype设置为 COMM_PERC 时,参数commission是否必须理解为 XX%或 0.XX

    如果此参数为 True:0.XX 如果此参数为 False:XX%

get_leverage()

返回此佣金方案允许的杠杆水平

getsize(price, cash)

返回在给定价格下执行现金操作所需的大小

getoperationcost(size, price)

返回操作将花费的现金金额

getvaluesize(size, price)

返回给定价格的大小值。对于类似期货的对象,它在size * margin处固定。

getvalue(position, price)

返回给定价格的位置值。对于类似期货的对象,它在size * margin处固定。

get_margin(price)

返回给定价格下资产单个项目所需的实际保证金/担保。默认实现具有以下策略:

  • 如果参数automargin评估为False,则使用参数margin

  • 使用参数mult,即如果automargin < 0,则为mult * price

  • 使用参数automargin,即如果automargin > 0,则为automargin * price

getcommission(size, price)

计算给定价格的操作的佣金

_getcommission(size, price, pseudoexec)

计算给定价格的操作的佣金

pseudoexec:如果为 True,则操作尚未执行

profitandloss(size, price, newprice)

返回位置的实际盈亏

cashadjust(size, price, newprice)

计算给定价格差异的现金调整

get_credit_interest(data, pos, dt)

计算卖空或特定产品的信用费用

_get_credit_interest(data, size, price, days, dt0, dt1)

此方法返回经纪人收取的信用利息成本。

如果size > 0,则仅在类的参数interest_longTrue时才调用此方法。

计算信用利率的公式为:

公式:days * price * abs(size) * (interest / 365)

参数:

* `data`: data feed for which interest is charged

* `size`: current position size. > 0 for long positions and < 0 for
  short positions (this parameter will not be `0`)

* `price`: current position price

* `days`: number of days elapsed since last credit calculation
  (this is (dt0 - dt1).days)

* `dt0`: (datetime.datetime) current datetime

* `dt1`: (datetime.datetime) datetime of previous calculation

dt0dt1在默认实现中未被使用,并作为覆盖方法的额外输入提供

扩展佣金

原文:www.backtrader.com/docu/extending-commissions/commission-schemes-extended/

佣金和相关功能由一个名为CommissionInfo的单个类管理,该类大部分通过调用broker.setcommission实例化。

该概念仅限于具有保证金和按合同固定佣金的期货,以及具有基于价格/数量百分比的股票。即使已实现其目的,也不是最灵活的方案。

GitHub 上的增强请求#29导致一些重写,以便:

  • 保持CommissionInfobroker.setcommission与原始行为兼容。

  • 对代码进行一些清理

  • 使佣金方案灵活以支持增强请求和进一步的可能性

在进入示例之前的实际工作

class CommInfoBase(with_metaclass(MetaParams)):
    COMM_PERC, COMM_FIXED = range(2)

    params = (
        ('commission', 0.0), ('mult', 1.0), ('margin', None),
        ('commtype', None),
        ('stocklike', False),
        ('percabs', False),
    )

引入了CommissionInfo的基类,它将新参数添加到混合中:

  • commtype(默认:None

    这是兼容性的关键。如果值为None,则CommissionInfo对象和broker.setcommission的行为将与以前相同。即:

    • 如果设置了margin,则佣金方案适用于具有按合同固定佣金的期货

    • 如果未设置margin,则佣金方案适用于采用百分比方法的股票。

    如果值为COMM_PERCCOMM_FIXED(或从派生类中的任何其他值),这显然决定了佣金是否是固定的还是基于百分比的。

  • stocklike(默认:False

    如上所述,旧的CommissionInfo对象中的实际行为由参数margin确定

    如果commtype设置为None之外的其他内容,则此值指示资产是否为类似期货的资产(将使用保证金并进行基于条形的现金调整),否则这是类似股票的资产。

  • percabs(默认:False

    如果为False,则百分比必须以相对形式传递(xx%)。

    如果为True,则百分比必须作为绝对值传递(0.xx)

    CommissionInfo是从CommInfoBase继承的,将此参数的默认值更改为True以保持兼容的行为。

所有这些参数也可以在broker.setcommission中使用,现在看起来像这样:

def setcommission(self,
                  commission=0.0, margin=None, mult=1.0,
                  commtype=None, percabs=True, stocklike=False,
                  name=None):

请注意以下内容:

  • percabsTrue,以保持与旧调用的兼容行为,如上所述对于CommissionInfo对象

用于测试commissions-schemes的旧示例已重写以支持命令行参数和新行为。用法帮助:

$ ./commission-schemes.py --help
usage: commission-schemes.py [-h] [--data DATA] [--fromdate FROMDATE]
                             [--todate TODATE] [--stake STAKE]
                             [--period PERIOD] [--cash CASH] [--comm COMM]
                             [--mult MULT] [--margin MARGIN]
                             [--commtype {none,perc,fixed}] [--stocklike]
                             [--percrel] [--plot] [--numfigs NUMFIGS]

Commission schemes

optional arguments:
  -h, --help            show this help message and exit
  --data DATA, -d DATA  data to add to the system (default:
                        ../../datas/2006-day-001.txt)
  --fromdate FROMDATE, -f FROMDATE
                        Starting date in YYYY-MM-DD format (default:
                        2006-01-01)
  --todate TODATE, -t TODATE
                        Starting date in YYYY-MM-DD format (default:
                        2006-12-31)
  --stake STAKE         Stake to apply in each operation (default: 1)
  --period PERIOD       Period to apply to the Simple Moving Average (default:
                        30)
  --cash CASH           Starting Cash (default: 10000.0)
  --comm COMM           Commission factor for operation, either apercentage or
                        a per stake unit absolute value (default: 2.0)
  --mult MULT           Multiplier for operations calculation (default: 10)
  --margin MARGIN       Margin for futures-like operations (default: 2000.0)
  --commtype {none,perc,fixed}
                        Commission - choose none for the old CommissionInfo
                        behavior (default: none)
  --stocklike           If the operation is for stock-like assets orfuture-
                        like assets (default: False)
  --percrel             If perc is expressed in relative xx{'const': True,
                        'help': u'If perc is expressed in relative xx%
                        ratherthan absolute value 0.xx', 'option_strings': [u'
                        --percrel'], 'dest': u'percrel', 'required': False,
                        'nargs': 0, 'choices': None, 'default': False, 'prog':
                        'commission-schemes.py', 'container':
                        <argparse._ArgumentGroup object at
                        0x0000000007EC9828>, 'type': None, 'metavar':
                        None}atherthan absolute value 0.xx (default: False)
  --plot, -p            Plot the read data (default: False)
  --numfigs NUMFIGS, -n NUMFIGS
                        Plot using numfigs figures (default: 1)

让我们进行一些运行以重现原始佣金方案贴文的原始行为。

期货佣金(固定和有保证金)

执行和图表:

$ ./commission-schemes.py --comm 2.0 --margin 2000.0 --mult 10 --plot

image

并且输出显示固定佣金为 2.0 货币单位(默认的投注是 1):

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 2.00
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 2.00
2006-04-12, TRADE PROFIT, GROSS 328.00, NET 324.00
...

股票佣金(带和不带保证金的百分比)

执行和图表:

$ ./commission-schemes.py --comm 0.005 --margin 0 --mult 1 --plot

图片

为了提高可读性,可以使用相对百分比值:

$ ./commission-schemes.py --percrel --comm 0.5 --margin 0 --mult 1 --plot

现在0.5直接意味着0.5%

在两种情况下输出为:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 3754.13, Comm 18.77
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 3754.13, Comm 18.93
2006-04-12, TRADE PROFIT, GROSS 32.80, NET -4.91
...

期货的佣金(百分比和有保证金)

使用新参数,期货的基于百分比的方案:

$ ./commission-schemes.py --commtype perc --percrel --comm 0.5 --margin 2000 --mult 10 --plot

图片

改变佣金……最终结果改变也就不足为奇了。

输出显示佣金现在是可变的:

2006-03-09, BUY CREATE, 3757.59
2006-03-10, BUY EXECUTED, Price: 3754.13, Cost: 2000.00, Comm 18.77
2006-04-11, SELL CREATE, 3788.81
2006-04-12, SELL EXECUTED, Price: 3786.93, Cost: 2000.00, Comm 18.93
2006-04-12, TRADE PROFIT, GROSS 328.00, NET 290.29
...

在先前的运行中设置了 2.0 货币单位(默认投注为 1)

另一篇文章将详细介绍新类别和一种自制佣金方案的实现。

示例的代码

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt
import backtrader.feeds as btfeeds
import backtrader.indicators as btind

class SMACrossOver(bt.Strategy):
    params = (
        ('stake', 1),
        ('period', 30),
    )

    def log(self, txt, dt=None):
  ''' Logging function fot this strategy'''
        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]:
            # Buy/Sell order submitted/accepted to/by broker - Nothing to do
            return

        # Check if an order has been completed
        # Attention: broker could reject order if not enougth cash
        if order.status in [order.Completed, order.Canceled, order.Margin]:
            if order.isbuy():
                self.log(
                    'BUY EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                    (order.executed.price,
                     order.executed.value,
                     order.executed.comm))
            else:  # Sell
                self.log('SELL EXECUTED, Price: %.2f, Cost: %.2f, Comm %.2f' %
                         (order.executed.price,
                          order.executed.value,
                          order.executed.comm))

    def notify_trade(self, trade):
        if trade.isclosed:
            self.log('TRADE PROFIT, GROSS %.2f, NET %.2f' %
                     (trade.pnl, trade.pnlcomm))

    def __init__(self):
        sma = btind.SMA(self.data, period=self.p.period)
        # > 0 crossing up / < 0 crossing down
        self.buysell_sig = btind.CrossOver(self.data, sma)

    def next(self):
        if self.buysell_sig > 0:
            self.log('BUY CREATE, %.2f' % self.data.close[0])
            self.buy(size=self.p.stake)  # keep order ref to avoid 2nd orders

        elif self.position and self.buysell_sig < 0:
            self.log('SELL CREATE, %.2f' % self.data.close[0])
            self.sell(size=self.p.stake)

def runstrategy():
    args = parse_args()

    # Create a cerebro
    cerebro = bt.Cerebro()

    # Get the dates from the args
    fromdate = datetime.datetime.strptime(args.fromdate, '%Y-%m-%d')
    todate = datetime.datetime.strptime(args.todate, '%Y-%m-%d')

    # Create the 1st data
    data = btfeeds.BacktraderCSVData(
        dataname=args.data,
        fromdate=fromdate,
        todate=todate)

    # Add the 1st data to cerebro
    cerebro.adddata(data)

    # Add a strategy
    cerebro.addstrategy(SMACrossOver, period=args.period, stake=args.stake)

    # Add the commission - only stocks like a for each operation
    cerebro.broker.setcash(args.cash)

    commtypes = dict(
        none=None,
        perc=bt.CommInfoBase.COMM_PERC,
        fixed=bt.CommInfoBase.COMM_FIXED)

    # Add the commission - only stocks like a for each operation
    cerebro.broker.setcommission(commission=args.comm,
                                 mult=args.mult,
                                 margin=args.margin,
                                 percabs=not args.percrel,
                                 commtype=commtypes[args.commtype],
                                 stocklike=args.stocklike)

    # And run it
    cerebro.run()

    # Plot if requested
    if args.plot:
        cerebro.plot(numfigs=args.numfigs, volume=False)

def parse_args():
    parser = argparse.ArgumentParser(
        description='Commission schemes',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,)

    parser.add_argument('--data', '-d',
                        default='../../datas/2006-day-001.txt',
                        help='data to add to the system')

    parser.add_argument('--fromdate', '-f',
                        default='2006-01-01',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--todate', '-t',
                        default='2006-12-31',
                        help='Starting date in YYYY-MM-DD format')

    parser.add_argument('--stake', default=1, type=int,
                        help='Stake to apply in each operation')

    parser.add_argument('--period', default=30, type=int,
                        help='Period to apply to the Simple Moving Average')

    parser.add_argument('--cash', default=10000.0, type=float,
                        help='Starting Cash')

    parser.add_argument('--comm', default=2.0, type=float,
                        help=('Commission factor for operation, either a'
                              'percentage or a per stake unit absolute value'))

    parser.add_argument('--mult', default=10, type=int,
                        help='Multiplier for operations calculation')

    parser.add_argument('--margin', default=2000.0, type=float,
                        help='Margin for futures-like operations')

    parser.add_argument('--commtype', required=False, default='none',
                        choices=['none', 'perc', 'fixed'],
                        help=('Commission - choose none for the old'
                              ' CommissionInfo behavior'))

    parser.add_argument('--stocklike', required=False, action='store_true',
                        help=('If the operation is for stock-like assets or'
                              'future-like assets'))

    parser.add_argument('--percrel', required=False, action='store_true',
                        help=('If perc is expressed in relative xx% rather'
                              'than absolute value 0.xx'))

    parser.add_argument('--plot', '-p', action='store_true',
                        help='Plot the read data')

    parser.add_argument('--numfigs', '-n', default=1,
                        help='Plot using numfigs figures')

    return parser.parse_args()

if __name__ == '__main__':
    runstrategy()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/544789.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎么做预约小程序_探索我们的全新预约小程序

在繁忙的现代生活中&#xff0c;无论是想预约一次美容护理&#xff0c;还是预定一家心仪的餐厅&#xff0c;亦或是安排一次专业的咨询服务&#xff0c;我们都希望能够在最短的时间内完成这些操作&#xff0c;节省时间和精力。如今&#xff0c;一款全新的预约小程序应运而生&…

SSH安全设置

今天发现自己的公有云服务器被攻击了 然后查看了登录日志&#xff0c;如上图 ls -sh /var/log/secure vim /var/log/secure然后增加了安全相关的设置 具体可以从以下方面增加安全性&#xff1a; 修改默认SSH端口公有云修改安全组策略及防火墙端口设置登录失败次数锁定用户及限…

亚马逊CloudFront使用体验

前言 首先在体验CloudFront之前&#xff0c;先介绍一下什么是CDN&#xff0c;以及CDN的基本原理。 CDN是Content Delivery Network&#xff08;内容分发网络&#xff09;的缩写&#xff0c;是一种利用分布式节点技术&#xff0c;在全球部署服务器&#xff0c;即时地将网站、应…

LSTM 循环神经网络原理深度解读与网络结构精细剖析

长短期记忆网络&#xff08;Long Short-Term Memory, LSTM&#xff09;是一种特殊的循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;&#xff0c;设计用于解决长期依赖问题&#xff0c;特别是在处理时间序列数据时。 循环神经网络&#xff08;RNN&#xf…

每日一VUE——组件的生命周期

文章目录 VUE组件的生命周期生命周期钩子函数实例创建Teleport VUE组件的生命周期 组件生命周期 组件从创建到挂载、更新、到销毁的一系列过程被称为组件的生命周期。 生命周期函数 在组件的各个生命周期节点执行的函数&#xff0c;为生命周期钩子函数。 生命周期钩子函数…

RT-thread信号量与互斥量

1,信号量 信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。理解资源计数适合于线程间工作处理速度不匹配的场合;信号量在大于0时才能获取,在中断、线程中均可释放信号量。 为了体现使用信号量来达到线程间的同步,…

【STM32】西南交大嵌入式系统设计实验:环境配置

把走过的坑记录一下&#xff0c;希望后来人避坑 No ST-Link device detected.问题解决 如果跟着指导书出现这个问题&#xff1a; 直接跳过这一步不用再更新固件&#xff0c;后面直接创建项目写程序就行了。 在keil里配置成用DAP_link即可。 详细的可以看这篇文章&#xff1a…

NumPy基础及取值操作

文章目录 第1关&#xff1a;ndarray对象第2关&#xff1a;形状操作第3关&#xff1a;基础操作第4关&#xff1a;随机数生成第5关&#xff1a;索引与切片 第1关&#xff1a;ndarray对象 编程要求 根据提示&#xff0c;在右侧编辑器Begin-End中填充代码&#xff0c;根据测试用例…

Oracle 数据库 count的优化-避免全表扫描

Oracle 数据库 count的优化-避免全表扫描 select count(*) from t1; 这句话比较简单&#xff0c;但很有玄机&#xff01;对这句话运行的理解&#xff0c;反映了你对数据库的理解深度&#xff01; 建立实验的大表他t1 SQL> conn scott/tiger 已连接。 SQL> drop table …

Fiddler工具的操作和功能时-----定位到步骤图(助力抓包)

前言&#xff1a; 继续上一篇&#xff0c;已经对fiddler的安装、配置和代理的问题进行了讲解&#xff1a; Fiddle配置代理&#xff0c;保手机模拟器访问外部网络-CSDN博客 本章&#xff0c;讲对一些fiddler的操作进行一系列讲解&#xff01;Fiddler作为一款网络调试工具&…

可视化大屏的应用(13):3D建模运用到机房运维中

可视化大屏在机房运维中发挥着重要的作用&#xff0c;主要体现在以下几个方面&#xff1a; 实时监控 通过可视化大屏&#xff0c;可以实时监控机房的各项指标和状态&#xff0c;如服务器的运行状态、网络流量、机房温度等。运维人员可以通过大屏快速获取关键信息&#xff0c;…

LazyVim开发vue2

neovim 0.5刚出来的时代&#xff0c;那时刚有lua插件我很狂热。每天沉迷于打造自己的IDE之中。写过一堆相关的博客&#xff0c;也录过一些视频教程。后来发现neovim的接口和插件更新的很快&#xff0c;导致配置文件要不定期的修改&#xff0c;才能保证新版本的插件的适配。我也…

程序员学CFA——数量分析方法(三)

数量分析方法&#xff08;三&#xff09; 概率论基础概率论的基本概念概率论的相关术语随机变量结果随机事件 事件之间的关系互斥事件遍历事件独立事件 概率的定义与确定方法概率的定义概率的确定方法赔率条件概率 概率的计算乘法法则与加法法则联合概率与乘法法则加法法则 全概…

LeetCode257:二叉树的所有路径

题目描述 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 解题思想 利用了回溯 代码 class Solution { public:void traversal(TreeNode* node, vector<int> &path, vect…

哈希 | unordered_set + unordered_map 的模拟实现(上)

目录 什么是 unordered_set unordered_map &#xff1f; unordered_set &#xff1a; unordered_map &#xff1a; 哈希 哈希表&#xff1a; 哈希冲突&#xff1a; 如何解决哈希冲突&#xff1a; 闭散列&#xff1a; 负载因子&#xff1a; 闭散列的模拟实现&#xff…

html公众号页面实现点击按钮跳转到导航

实现效果&#xff1a; 点击导航自动跳转到&#xff1a; html页面代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>跳转导航</title><meta name"keywords" conten…

【学习笔记十五】批次管理和容量管理

一、批次管理 1.配置 SAP EWM 特定参数 激活仓库的批次管理 2.ERP端物料需要启用批次管理 3.EWM物料需要启用批次管理 一般是ERP启用批次管理&#xff0c;相关的配置也会传输到EWM系统 4.建立批次主数据 5.创建采购订单并创建内向交货单&#xff0c;维护批次 6.维护产品主数…

【Canvas技法】绘制正三角形、切角正三角形、圆角正三角形

【图例】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>绘制正三角形、切角正三角形、圆角正三角形</title><style …

计算机网络—传输层UDP协议:原理、应用

​ &#x1f3ac;慕斯主页&#xff1a;修仙—别有洞天 ♈️今日夜电波&#xff1a;2月のセプテンバー 1:21━━━━━━️&#x1f49f;──────── 5:21 &#x1f504; ◀️ ⏸ ▶️ ☰ &am…
最新文章