当前位置: 首页 > news >正文

Backtrader从0到1——第一个回测策略

Backtrader从0到1——第一个回测策略

  • 0. 前言
  • 1. lines && index
  • 2. 生成大脑
  • 3. 设置起始资金和佣金
  • 4. 添加数据(重点)
  • 5. 第一个策略——双均线
    • 5.1 策略类
    • 5.2 策略参数
    • 5.3 添加指标
    • 5.4 买卖与订单order
    • 5.5 完整策略代码


0. 前言


本人翻阅了大量资料,以及市面上的Backtrade教程,大多都晦涩难懂,或者模棱两可,也不系统。迫于无奈,只能通过Backtrader的官方文档慢慢学习。可官方文档是英文的,使用翻译软件翻译的结果,也差强人意,只能凭借着即将消失的英文记忆,慢慢啃原文。

于是,笔者决定,将啃到的内容做一下整理,分享给大家。也是做一个Backtrader的系统性教程,希望大家支持。本系列文章除了讲解文档中的内容外,还有一些我自己的理解。

笔者不生产文档,只是文档的搬运工!!!


1. lines && index


要学习Backtrader,首先需要了解其中两个核心概念,linesindex

1. lines

  • 股票等金融产品中,往往有如下几个基本数据:
    • Open(开盘价),High(最高价),Low(最低价),Close(收盘价), Volume(成交量),OpenInterest(持仓量)。
  • 这些基本数据,沿着时间看,就是一条一条的线。因此一个数据源Feed中,通常存在6个lines。如果算上时间,就有7个lines

在这里插入图片描述

2. index

  • 在Python中,可以用索引0访问数组的第一个元素;
  • 在Backtrader中,索引0表示lines中当前的值,1表示下一个值,-1表示上一个值,当然还有2,-2,3…以此类推(这里不理解没关系,后面结合代码理解,有个印象即可,即Backtrader中lines的索引和传统意义上的索引不一样)。

2. 生成大脑


1. 要知道,所谓量化回测,是模拟人的思维方式,自动化的进行回测。在这个过程中,我们要再现人的思考过程。表现在代码中就是:

  • a. 要思考,要先有一个脑子cerebro,通过bt.Cerebro()实例化出一个;
  • b. 有了脑子之后,还要让脑子转起来,思考起来cerebro.run()
import backtrader as btif __name__ == '__main__':cerebro = bt.Cerebro()	# 创建决策大脑print('初始投资组合总价值: %.2f' % cerebro.broker.getvalue())cerebro.run()	# 大脑开始思考print('最终投资组合总价值: %.2f' % cerebro.broker.getvalue())
  • 可是这个大脑里面空空如也,什么也没有,所以此时cerebro.run()就相当于是人在发呆了,什么也没干。

  • 上面代码中用到了cerebro.broker.getvalue()方法,作用是返回当前投资组合的总价值,计算公式为:总价值 = 现金 + ∑(持仓数量 × 最新价格)。

2. 输出结果

初始投资组合总价值: 10000.00
最终投资组合总价值: 10000.00
  • 可以看到,这个实例化出的脑子中,有一个初始总价值,为1W。对一些经济商来说,1W通常是一个起始价值。

broker的意思是经纪人,在早些时候,我们要想知道自己在股市中有多少钱,是需要到证券公司,找经纪人确认的。这里就是模拟了你向经纪人询问自己有多少钱的行为。后面这个broker我们还会经常用到。


3. 设置起始资金和佣金


1W太少了,我要拿10W进股市!!!

  • 通过setcash()方法,可以设置初始资金。通过setcommission()方法设置佣金,如果是1%就填0.01,1除以100。
import backtrader as btif __name__ == '__main__':cerebro = bt.Cerebro()	# 创建决策大脑cerebro.broker.setcash(100000.0)cerebro.broker.setcommission(0.0002) # 0.02%的佣金print('初始投资组合总价值: %.2f' % cerebro.broker.getvalue())cerebro.run()	# 大脑开始思考print('最终投资组合总价值: %.2f' % cerebro.broker.getvalue())
  • 输出:
初始投资组合总价值: 100000.00
最终投资组合总价值: 100000.00

4. 添加数据(重点)


数据源来自akshare,大家可以自行去官方文档查看相关接口。https://akshare.akfamily.xyz/index.html

1. 整理数据格式

  • Backtrader需要的数据要有6个基本属性,可以通过bt.feeds.PandasData(dataname=, fromdate=, todate=)构造:
    • Open(开盘价),High(最高价),Low(最低价),Close(收盘价), Volume(成交量),OpenInterest(持仓量)。
  • Backtrade需要的数据对索引十分敏感,必须是时间类型。
import akshare as ak
import pandas as pd# 1. 获取股票日线数据(akshare)
def GetStockDataDailyAK(code, fromdate, todate):"""code: str,股票代码formdate: date,开始日期todate: date,结束日期"""fromdate = fromdate.strftime('%Y%m%d')todate = todate.strftime('%Y%m%d')# 获取某只股票的日线级别前复权K线数据df = ak.stock_zh_a_hist(symbol=code, period="daily", start_date=fromdate, end_date=todate, adjust="qfq")# 改列名,让之后的操作更符合backtrade一般的操作习惯df = df.rename(columns={'日期':'date', '开盘':'open', '收盘':'close', '最高':'high', '成交量':'volume', '最低':'low'})# 新增一列 openinterestdf['openinterest'] = 0# 设置索引为日期格式df.index = pd.to_datetime(df.date)# 对dateframe的数据列进行整合,使其符合backtrader期望的数据格式df = df[['open', 'high', 'low', 'close', 'volume', 'openinterest']]return df

2. 添加数据

  • 使用bt.feeds.PandasData()方法构造数据源后,使用cerebro.adddata()方法将数据塞进大脑。
if __name__=='__main__':fromdate = datetime(2017, 1, 1)todate = datetime(2020, 1, 1)# 获取茅台的数据stock_df = GetStockDataDailyAK('600519', fromdate, todate)# 加载并读取数据源,fromdate(日期格式),todate(日期格式)data = bt.feeds.PandasData(dataname=stock_df, fromdate=fromdate, todate=todate)# 3. 策略设置cerebro = bt.Cerebro() # 创建大脑# 将数据加入回测系统cerebro.adddata(data)# 经纪人startcash = 100000 # 初始资金为10Wcerebro.broker.setcash(startcash)# 4. 执行回测print('初始总价值: %.2f' % cerebro.broker.getvalue())cerebro.run()print('最终总价值: %.2f' % cerebro.broker.getvalue())

5. 第一个策略——双均线


5.1 策略类


Backtrader中,策略也是一个类,需要继承bt.Strategy

1. 我们来实现一个简单的策略,打印每一天的收盘价:

class MyStrategy(bt.Strategy):def log(self, txt, dt=None):''' Logging function for this strategy'''dt = dt or self.datas[0].datetime.date(0)print('%s, %s' % (dt.isoformat(), txt))def __init__(self):# Keep a reference to the "close" line in the data[0] dataseriesself.dataclose = self.datas[0].closedef next(self):# Simply log the closing price of the series from the referenceself.log('Close, %.2f' % self.dataclose[0])

2. 里面有几个核心功能:

  • log
    • 日志功能,通常在进行一些操作时进行日志记录,如买入和卖出。使用方法也很简单,直接调用self.log(txt),传一个字符串参数txt即可。打印格式为“日期,txt”。
  • init
    • 初始化功能,通常用来定义一些成员变量。这里需要着重解释一下,cerebro.adddata()方法可以执行多次,就是说可以向大脑中加入多个类似于1中的表格,也叫多个lines。使用self.datas[0]就访问到了第一次加入的lines。再.close就拿出了close对应的这一列。所以self.dataclose就是一条line,之后可以通过索引访问。
  • next
    • 策略执行的核心方法,Backtrader会遍一行一行的遍历lines。通过self.dataclose[0],就可以访问到遍历位置的当前收盘价。

3. 策略加入大脑并执行

  • cerebro.addstrategy(MyStrategy)知识涌进大脑!
if __name__=='__main__':fromdate = datetime(2017, 1, 1)todate = datetime(2020, 1, 1)# 获取茅台的数据stock_df = GetStockDataDailyAK('600519', fromdate, todate)# 加载并读取数据源,fromdate(日期格式),todate(日期格式)data = bt.feeds.PandasData(dataname=stock_df, fromdate=fromdate, todate=todate)# 3. 策略设置cerebro = bt.Cerebro() # 创建大脑# 将数据加入回测系统cerebro.adddata(data)# 加入自己的策略cerebro.addstrategy(MyStrategy)# 经纪人startcash = 100000 # 初始资金为10Wcerebro.broker.setcash(startcash)# 设置手续费cerebro.broker.setcommission(0.0002)# 4. 执行回测s = fromdate.strftime("%Y-%m-%d")t = todate.strftime("%Y-%m-%d")print('初始总价值: %.2f' % cerebro.broker.getvalue())cerebro.run()print('最终总价值: %.2f' % cerebro.broker.getvalue())
  • 输出:
初始总价值: 100000.00
2017-01-03, Close, 122.56
2017-01-04, Close, 139.91
2017-01-05, Close, 134.74
2017-01-06, Close, 138.76
2017-01-09, Close, 136.51
2017-01-10, Close, 137.00
...
2019-12-27, Close, 983.32
2019-12-30, Close, 1006.12
2019-12-31, Close, 1003.32
最终总价值: 100000.00
  • 之前我们的大脑只能发呆,现在至少可以做一些简单的“思考”了,不错,再接再厉。

5.2 策略参数


将策略中的某些值硬编码,并且无法轻松更改,这有点不切实际。参数可以派上用场。

1. 参数的定义很简单,如下所示:

class MyStrategy(bt.Strategy):params = (('myparam', 27),('exitbars', 5),)

2. 添加策略时,可以指定参数的值

# Add a strategy
cerebro.addstrategy(TestStrategy, myparam=20, exitbars=7)

5.3 添加指标


指标相信大家都不陌生,就是买卖的信号嘛。今天我们就为策略添加一个最最常见的指标,20日均线。

添加20日均线self.ma

  • Backtrader中,提供了很多方便的指标计算方法,其中就包括均线;
  • bt.indicators.SimpleMovingAverage()可以计算简单移动平均线,第一个参数传入收盘价,line格式,第二个参数传周期,因为我们是20日均线,所以传20即可。
class MyStrategy(bt.Strategy):params = (('maperiod', 20),)def __init__(self):self.ma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)# 引用到输入数据的close价格self.dataclose = self.datas[0].close
  • 该方法返回的也是一条line,可以通过Backtrader特殊的索引形式访问。
  • 需要注意的是,该指标至少需要20天,才可以算出一个,也就意味着,next方法将从lines的20行开始遍历。

5.4 买卖与订单order


1. notify_order模块

  • 这里要向大家普及一个基本常识,当我们想买入一支股票时,首先要将这个买入请求,报到场内,这个买入请求就是一个订单。
  • 当一个订单报到场内后,会有如下几种情况:
    • a. 订单已提交,但尚未被经纪商接受;
    • b. 订单已被经纪商接受,但尚未执行;
    • c. 订单执行,并已完成;
    • d. 订单被取消、保证金不足或拒绝。
  • 重点关心订单被取消的情况,多数情况下是因为资金不够了,买不起了。
  • notify_order模块就模拟了订单在场内的各种情况。订单处理完后要及时置空。
class MyStrategy(bt.Strategy):...def notify_order(self, order):if order.status in [order.Submitted]:# 订单已提交,但尚未被经纪商接受self.log('ORDER SUBMITTED')self.order = orderelif order.status in [order.Accepted]:# 订单已被经纪商接受,但尚未执行self.log('ORDER Accepted')self.order = orderelif order.status in [order.Completed]:# 订单执行,并已完成if order.isbuy():self.log('BUY EXECUTED, %.2f' % order.executed.price)else:self.log('SELL EXECUTED, %.2f' % order.executed.price)self.order = Noneelif order.status in [order.Canceled, order.Margin, order.Rejected]:# 订单被取消、保证金不足或拒绝self.log('ORDER CANCELED/MARGIN/REJECTED')self.order = None

1. 买卖

  • 我们指定一个很经典的买卖策略,当天收盘价上穿20日均线就买入,下穿20日均线就卖出。
  • 买卖用buy()/sell()方法即可,该方法会返回一个订单类型,用self.order接收,这是一个继承自父类的成员变量。
class MyStrategy(bt.Strategy):...def next(self):# 判断是否有订单正在进行if(self.order):return# 空仓(当前没有持仓)if not self.position:if self.datas[0].close[0] > self.ma[0]:self.order = self.buy(size=200)  # 买入两百股else:if self.datas[0].close[0] < self.ma[0]:self.order = self.sell(size=200) # 卖出两百股
  • 参数size可以指定买入多少股。

5.5 完整策略代码


import pandas as pd
from datetime import datetime
import backtrader as bt
import akshare as ak# 1. 获取股票日线数据(akshare)
def GetStockDataDailyAK(code, fromdate, todate):"""code: str,股票代码formdate: date,开始日期todate: date,结束日期"""fromdate = fromdate.strftime('%Y%m%d')todate = todate.strftime('%Y%m%d')# 获取某只股票的日线级别前复权K线数据df = ak.stock_zh_a_hist(symbol=code, period="daily", start_date=fromdate, end_date=todate, adjust="qfq")# 改列名,让之后的操作更符合backtrade一般的操作习惯df = df.rename(columns={'日期':'date', '开盘':'open', '收盘':'close', '最高':'high', '成交量':'volume', '最低':'low'})# 新增一列 openinterestdf['openinterest'] = 0# 设置索引为日期格式df.index = pd.to_datetime(df.date)# 对dateframe的数据列进行整合,使其符合backtrader期望的数据格式df = df[['open', 'high', 'low', 'close', 'volume', 'openinterest']]return df# 2. 构建策略:上穿20日均线买入,跌穿20日均线卖出
class MyStrategy(bt.Strategy):params = (('maperiod', 20),)def __init__(self):self.order = Noneself.ma = bt.indicators.SimpleMovingAverage(self.datas[0], period=self.params.maperiod)# 引用到输入数据的close价格self.dataclose = self.datas[0].closedef 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]:# 订单已提交,但尚未被经纪商接受self.log('ORDER SUBMITTED')self.order = orderelif order.status in [order.Accepted]:# 订单已被经纪商接受,但尚未执行self.log('ORDER Accepted')self.order = orderelif order.status in [order.Completed]:# 订单执行,并已完成if order.isbuy():self.log('BUY EXECUTED, %.2f' % order.executed.price)else:self.log('SELL EXECUTED, %.2f' % order.executed.price)self.order = Noneelif order.status in [order.Canceled, order.Margin, order.Rejected]:# 订单被取消、保证金不足或拒绝self.log('ORDER CANCELED/MARGIN/REJECTED')self.order = Nonedef next(self):# 判断是否有交易指令正在进行if(self.order):return# 空仓(当前没有持仓)if not self.position:if self.datas[0].close[0] > self.ma[0]:self.order = self.buy(size=200)  # 买入两百股else:if self.datas[0].close[0] < self.ma[0]:self.order = self.sell(size=200) # 卖出两百股if __name__=='__main__':fromdate = datetime(2017, 1, 1)todate = datetime(2020, 1, 1)# 获取茅台的数据stock_df = GetStockDataDailyAK('600519', fromdate, todate)# 加载并读取数据源,fromdate(日期格式),todate(日期格式)data = bt.feeds.PandasData(dataname=stock_df, fromdate=fromdate, todate=todate)# 3. 策略设置cerebro = bt.Cerebro() # 创建大脑# 将数据加入回测系统cerebro.adddata(data)# 加入自己的策略cerebro.addstrategy(MyStrategy)# 经纪人startcash = 100000 # 初始资金为10Wcerebro.broker.setcash(startcash)# 设置手续费cerebro.broker.setcommission(0.0002)# 4. 执行回测s = fromdate.strftime("%Y-%m-%d")t = todate.strftime("%Y-%m-%d")print(f'初始资金:{startcash}\n回测时间:{s}   {t}')cerebro.run()portval = cerebro.broker.getvalue()print(f'剩余总资金:{portval}\n回测时间:{s}   {t}')

输出

初始资金:100000
回测时间:2017-01-01   2020-01-01
2017-02-14, ORDER SUBMITTED
2017-02-14, ORDER Accepted
2017-02-14, BUY EXECUTED, 137.54
2017-02-17, ORDER SUBMITTED
2017-02-17, ORDER Accepted
2017-02-17, SELL EXECUTED, 136.90
2017-02-20, ORDER SUBMITTED
2017-02-20, ORDER Accepted
2017-02-20, BUY EXECUTED, 138.41
2017-05-09, ORDER SUBMITTED
2017-05-09, ORDER Accepted
2017-05-09, SELL EXECUTED, 192.44
2017-05-10, ORDER SUBMITTED
2017-05-10, ORDER Accepted
2017-05-10, BUY EXECUTED, 196.40
2017-07-04, ORDER SUBMITTED
2017-07-04, ORDER Accepted
2017-07-04, SELL EXECUTED, 246.96
2017-07-12, ORDER SUBMITTED
2017-07-12, ORDER Accepted
2017-07-12, BUY EXECUTED, 260.58
2017-07-13, ORDER SUBMITTED
2017-07-13, ORDER Accepted
2017-07-13, SELL EXECUTED, 250.13
2017-07-21, ORDER SUBMITTED
2017-07-21, ORDER Accepted
2017-07-21, BUY EXECUTED, 261.74
2017-09-06, ORDER SUBMITTED
2017-09-06, ORDER Accepted
2017-09-06, SELL EXECUTED, 284.78
2017-09-14, ORDER SUBMITTED
2017-09-14, ORDER Accepted
2017-09-14, BUY EXECUTED, 287.77
2017-11-23, ORDER SUBMITTED
2017-11-23, ORDER Accepted
2017-11-23, SELL EXECUTED, 436.78
2017-12-12, ORDER SUBMITTED
2017-12-12, ORDER Accepted
2017-12-12, BUY EXECUTED, 453.48
2018-01-30, ORDER SUBMITTED
2018-01-30, ORDER Accepted
2018-01-30, SELL EXECUTED, 530.78
2018-02-01, ORDER SUBMITTED
2018-02-01, ORDER Accepted
2018-02-01, BUY EXECUTED, 561.78
2018-02-02, ORDER SUBMITTED
2018-02-02, ORDER Accepted
2018-02-02, SELL EXECUTED, 547.00
2018-02-23, ORDER SUBMITTED
2018-02-23, ORDER Accepted
2018-02-23, BUY EXECUTED, 541.78
2018-03-01, ORDER SUBMITTED
2018-03-01, ORDER Accepted
2018-03-01, SELL EXECUTED, 522.78
2018-03-02, ORDER SUBMITTED
2018-03-02, ORDER Accepted
2018-03-02, BUY EXECUTED, 532.78
2018-03-06, ORDER SUBMITTED
2018-03-06, ORDER Accepted
2018-03-06, SELL EXECUTED, 519.78
2018-03-09, ORDER SUBMITTED
2018-03-09, ORDER Accepted
2018-03-09, BUY EXECUTED, 522.33
2018-03-14, ORDER SUBMITTED
2018-03-14, ORDER Accepted
2018-03-14, SELL EXECUTED, 504.45
2018-03-15, ORDER SUBMITTED
2018-03-15, ORDER Accepted
2018-03-15, BUY EXECUTED, 522.80
2018-03-23, ORDER SUBMITTED
2018-03-23, ORDER Accepted
2018-03-23, SELL EXECUTED, 495.78
2018-04-25, ORDER SUBMITTED
2018-04-25, ORDER Accepted
2018-04-25, BUY EXECUTED, 483.78
2018-04-27, ORDER SUBMITTED
2018-04-27, ORDER Accepted
2018-04-27, SELL EXECUTED, 480.03
2018-05-08, ORDER SUBMITTED
2018-05-08, ORDER Accepted
2018-05-08, BUY EXECUTED, 492.81
2018-06-20, ORDER SUBMITTED
2018-06-20, ORDER Accepted
2018-06-20, SELL EXECUTED, 546.78
2018-06-21, ORDER SUBMITTED
2018-06-21, ORDER Accepted
2018-06-21, BUY EXECUTED, 571.00
2018-06-27, ORDER SUBMITTED
2018-06-27, ORDER Accepted
2018-06-27, SELL EXECUTED, 557.78
2018-07-13, ORDER SUBMITTED
2018-07-13, ORDER Accepted
2018-07-13, BUY EXECUTED, 548.08
2018-07-31, ORDER SUBMITTED
2018-07-31, ORDER Accepted
2018-07-31, SELL EXECUTED, 526.08
2018-08-28, ORDER SUBMITTED
2018-08-28, ORDER Accepted
2018-08-28, BUY EXECUTED, 498.78
2018-09-03, ORDER SUBMITTED
2018-09-03, ORDER Accepted
2018-09-03, SELL EXECUTED, 457.78
2018-09-05, ORDER SUBMITTED
2018-09-05, ORDER Accepted
2018-09-05, BUY EXECUTED, 480.78
2018-09-06, ORDER SUBMITTED
2018-09-06, ORDER Accepted
2018-09-06, SELL EXECUTED, 457.78
2018-09-19, ORDER SUBMITTED
2018-09-19, ORDER Accepted
2018-09-19, BUY EXECUTED, 472.36
2018-10-11, ORDER SUBMITTED
2018-10-11, ORDER Accepted
2018-10-11, SELL EXECUTED, 440.79
2018-10-23, ORDER SUBMITTED
2018-10-23, ORDER Accepted
2018-10-23, BUY EXECUTED, 494.78
2018-10-24, ORDER SUBMITTED
2018-10-24, ORDER Accepted
2018-10-24, SELL EXECUTED, 433.89
2018-12-03, ORDER SUBMITTED
2018-12-03, ORDER Accepted
2018-12-03, BUY EXECUTED, 394.78
2018-12-20, ORDER SUBMITTED
2018-12-20, ORDER Accepted
2018-12-20, SELL EXECUTED, 382.00
2019-01-02, ORDER SUBMITTED
2019-01-02, ORDER Accepted
2019-01-02, BUY EXECUTED, 415.76
2019-03-11, ORDER SUBMITTED
2019-03-11, ORDER Accepted
2019-03-11, SELL EXECUTED, 545.82
2019-03-12, ORDER SUBMITTED
2019-03-12, ORDER Accepted
2019-03-12, BUY EXECUTED, 569.60
2019-03-13, ORDER SUBMITTED
2019-03-13, ORDER Accepted
2019-03-13, SELL EXECUTED, 551.28
2019-03-14, ORDER SUBMITTED
2019-03-14, ORDER Accepted
2019-03-14, BUY EXECUTED, 562.78
2019-05-07, ORDER SUBMITTED
2019-05-07, ORDER Accepted
2019-05-07, SELL EXECUTED, 699.06
2019-05-30, ORDER SUBMITTED
2019-05-30, ORDER Accepted
2019-05-30, BUY EXECUTED, 715.78
2019-06-03, ORDER SUBMITTED
2019-06-03, ORDER Accepted
2019-06-03, SELL EXECUTED, 697.78
2019-06-12, ORDER SUBMITTED
2019-06-12, ORDER Accepted
2019-06-12, BUY EXECUTED, 712.00
2019-07-16, ORDER SUBMITTED
2019-07-16, ORDER Accepted
2019-07-16, SELL EXECUTED, 795.77
2019-07-30, ORDER SUBMITTED
2019-07-30, ORDER Accepted
2019-07-30, BUY EXECUTED, 796.82
2019-08-02, ORDER SUBMITTED
2019-08-02, ORDER Accepted
2019-08-02, SELL EXECUTED, 764.32
2019-08-09, ORDER SUBMITTED
2019-08-09, ORDER Accepted
2019-08-09, BUY EXECUTED, 798.82
2019-09-12, ORDER SUBMITTED
2019-09-12, ORDER Accepted
2019-09-12, SELL EXECUTED, 886.32
2019-09-19, ORDER SUBMITTED
2019-09-19, ORDER CANCELED/MARGIN/REJECTED
2019-09-20, ORDER SUBMITTED
2019-09-20, ORDER CANCELED/MARGIN/REJECTED
2019-09-23, ORDER SUBMITTED
2019-09-23, ORDER CANCELED/MARGIN/REJECTED
2019-09-24, ORDER SUBMITTED
2019-09-24, ORDER CANCELED/MARGIN/REJECTED
2019-09-25, ORDER SUBMITTED
2019-09-25, ORDER CANCELED/MARGIN/REJECTED
2019-09-26, ORDER SUBMITTED
2019-09-26, ORDER CANCELED/MARGIN/REJECTED
2019-09-27, ORDER SUBMITTED
2019-09-27, ORDER CANCELED/MARGIN/REJECTED
2019-09-30, ORDER SUBMITTED
2019-09-30, ORDER CANCELED/MARGIN/REJECTED
2019-10-08, ORDER SUBMITTED
2019-10-08, ORDER CANCELED/MARGIN/REJECTED
2019-10-09, ORDER SUBMITTED
2019-10-09, ORDER CANCELED/MARGIN/REJECTED
2019-10-10, ORDER SUBMITTED
2019-10-10, ORDER CANCELED/MARGIN/REJECTED
2019-10-11, ORDER SUBMITTED
2019-10-11, ORDER CANCELED/MARGIN/REJECTED
2019-10-14, ORDER SUBMITTED
2019-10-14, ORDER CANCELED/MARGIN/REJECTED
2019-10-15, ORDER SUBMITTED
2019-10-15, ORDER CANCELED/MARGIN/REJECTED
2019-10-16, ORDER SUBMITTED
2019-10-16, ORDER CANCELED/MARGIN/REJECTED
2019-10-17, ORDER SUBMITTED
2019-10-17, ORDER CANCELED/MARGIN/REJECTED
2019-10-18, ORDER SUBMITTED
2019-10-18, ORDER CANCELED/MARGIN/REJECTED
2019-10-21, ORDER SUBMITTED
2019-10-21, ORDER CANCELED/MARGIN/REJECTED
2019-10-22, ORDER SUBMITTED
2019-10-22, ORDER CANCELED/MARGIN/REJECTED
2019-10-23, ORDER SUBMITTED
2019-10-23, ORDER CANCELED/MARGIN/REJECTED
2019-10-28, ORDER SUBMITTED
2019-10-28, ORDER CANCELED/MARGIN/REJECTED
2019-10-29, ORDER SUBMITTED
2019-10-29, ORDER CANCELED/MARGIN/REJECTED
2019-10-30, ORDER SUBMITTED
2019-10-30, ORDER CANCELED/MARGIN/REJECTED
2019-10-31, ORDER SUBMITTED
2019-10-31, ORDER CANCELED/MARGIN/REJECTED
2019-11-01, ORDER SUBMITTED
2019-11-01, ORDER CANCELED/MARGIN/REJECTED
2019-11-04, ORDER SUBMITTED
2019-11-04, ORDER CANCELED/MARGIN/REJECTED
2019-11-05, ORDER SUBMITTED
2019-11-05, ORDER CANCELED/MARGIN/REJECTED
2019-11-06, ORDER SUBMITTED
2019-11-06, ORDER CANCELED/MARGIN/REJECTED
2019-11-07, ORDER SUBMITTED
2019-11-07, ORDER CANCELED/MARGIN/REJECTED
2019-11-08, ORDER SUBMITTED
2019-11-08, ORDER CANCELED/MARGIN/REJECTED
2019-11-11, ORDER SUBMITTED
2019-11-11, ORDER CANCELED/MARGIN/REJECTED
2019-11-12, ORDER SUBMITTED
2019-11-12, ORDER CANCELED/MARGIN/REJECTED
2019-11-13, ORDER SUBMITTED
2019-11-13, ORDER CANCELED/MARGIN/REJECTED
2019-11-14, ORDER SUBMITTED
2019-11-14, ORDER CANCELED/MARGIN/REJECTED
2019-11-15, ORDER SUBMITTED
2019-11-15, ORDER CANCELED/MARGIN/REJECTED
2019-11-18, ORDER SUBMITTED
2019-11-18, ORDER CANCELED/MARGIN/REJECTED
2019-11-19, ORDER SUBMITTED
2019-11-19, ORDER CANCELED/MARGIN/REJECTED
2019-11-20, ORDER SUBMITTED
2019-11-20, ORDER CANCELED/MARGIN/REJECTED
2019-11-21, ORDER SUBMITTED
2019-11-21, ORDER CANCELED/MARGIN/REJECTED
2019-11-22, ORDER SUBMITTED
2019-11-22, ORDER CANCELED/MARGIN/REJECTED
2019-12-18, ORDER SUBMITTED
2019-12-18, ORDER CANCELED/MARGIN/REJECTED
2019-12-19, ORDER SUBMITTED
2019-12-19, ORDER CANCELED/MARGIN/REJECTED
2019-12-30, ORDER SUBMITTED
2019-12-30, ORDER CANCELED/MARGIN/REJECTED
2019-12-31, ORDER SUBMITTED
2019-12-31, ORDER CANCELED/MARGIN/REJECTED
剩余总资金:192051.28319999998
回测时间:2017-01-01   2020-01-01

可以看到,钱将近翻了一倍,茅台还是猛啊!!!后来我们的订单请求一直被拒绝,是因为股票涨的太狠,我们买不起了!


相关文章:

Backtrader从0到1——第一个回测策略

Backtrader从0到1——第一个回测策略 0. 前言1. lines && index2. 生成大脑3. 设置起始资金和佣金4. 添加数据&#xff08;重点&#xff09;5. 第一个策略——双均线5.1 策略类5.2 策略参数5.3 添加指标5.4 买卖与订单order5.5 完整策略代码 0. 前言 本人翻阅了大量资料…...

GPT - 因果掩码(Causal Mask)

本节代码定义了一个函数 causal_mask&#xff0c;用于生成因果掩码&#xff08;Causal Mask&#xff09;。因果掩码通常用于自注意力机制中&#xff0c;以确保模型在解码时只能看到当前及之前的位置&#xff0c;而不能看到未来的信息。这种掩码在自然语言处理任务&#xff08;如…...

lombok的坑

我使用lombok的Data注解带来的坑。 代码如下&#xff1a; 公共类&#xff1a; package com.tyler.oshi.common;import lombok.Data; import lombok.NoArgsConstructor;/*** author: TylerZhong* description:*/Data NoArgsConstructor public class R {private int code;priv…...

基于Python的网络爬虫技术研究

基于Python的网络爬虫技术研究 以下从多个方面为你介绍基于 Python 的网络爬虫技术&#xff1a; 概述 网络爬虫是一种自动获取网页内容的程序&#xff0c;在 Python 中可以借助诸多强大的库和工具实现。网络爬虫能应用于数据采集、搜索引擎、舆情监测等众多领域。 核心库 …...

微信小程序跳6

//金额格式化 rmoney: function(money) { return parseFloat(money).toFixed(2).toString().split().reverse().join().replace(/(\d{3})/g, $1,) .replace( /\,$/, ).split().reverse().join(); }, daysUntil: function(milliseconds) { const endDate new Date(milliseconds…...

项目1笔记

Data Data 是一个常用的 Lombok 注解&#xff0c;主要用于 Java 类中&#xff0c;可以自动生成以下内容&#xff1a; Getter&#xff08;所有字段&#xff09; Setter&#xff08;所有非 final 字段&#xff09; toString() 方法 equals() 和 hashCode() 方法 无参构造函…...

分享:批量识别图片文字并重命名,根据图片文字内容对图片批量重命名,Python和Tesseract OCR的完成方案

一、项目背景 在日常工作中,处理大量图片文件时,常常需要从图片中提取文字信息,并根据提取的文字对图片进行重命名。传统的手动操作方式效率低下且容易出错。通过OCR(光学字符识别)技术,可以自动从图片中提取文字信息,并基于提取的文字对图片进行批量重命名。 Tesserac…...

【安全】加密算法原理与实战

为了理解SSL/TLS原理&#xff0c;大家需要掌握一些加密算法的基础知识。当然&#xff0c;这不是为了让大家成为密码学专家&#xff0c;所以只需对基础的加密算法有一些了解即可。基础的加密算法主要有哈希&#xff08;Hash&#xff0c;或称为散列&#xff09;​、对称加密(Symm…...

STM32STM8芯片擦除与读保护

连接STM单片机与断开单片机连接&#xff0c; 点击擦除就可以了。 文件选HEX在选择Verify进行下载。...

Qwen2.5技术报告阅读

论文概述 ⸻ &#x1f9e0; 1. 模型概述 Qwen2.5 是阿里巴巴推出的一系列大语言模型&#xff08;LLMs&#xff09;&#xff0c;在 预训练数据量 和 后训练方法 上都比前一代 Qwen2 有了显著提升。 ⸻ &#x1f4c8; 2. 模型特点 • 预训练数据量提升&#xff1a;从 7 万亿…...

HDCP(二)

HDCP加密算法实现详解 HDCP&#xff08;高带宽数字内容保护&#xff09;的加密算法实现涉及对称加密、密钥派生、动态同步机制等核心环节&#xff0c;其设计兼顾实时性与安全性。以下从算法类型、流程实现、硬件集成等角度展开分析&#xff1a; 1. 加密算法类型与版本差异 •…...

POSIX线程(pthread)库:线程的终止与管理

在POSIX线程&#xff08;pthread&#xff09;库中&#xff0c;线程的终止和管理涉及多个关键函数。以下是关于线程终止的pthread系列函数的详细介绍&#xff1a; 1. pthread_exit&#xff1a;线程主动退出 ✨ 功能&#xff1a; 允许线程主动终止自身&#xff0c;并返回一个退出…...

Elasticsearch 系列专题 - 第三篇:搜索与查询

搜索是 Elasticsearch 的核心功能之一。本篇将介绍如何构建高效的查询、优化搜索结果,以及调整相关性评分,帮助你充分发挥 Elasticsearch 的搜索能力。 1. 基础查询 1.1 Match Query 与 Term Query 的区别 Match Query:用于全文搜索,会对查询词进行分词。 GET /my_index/_…...

【AI提示词】Emoji风格排版艺术与设计哲学

提示说明 Emoji风格排版艺术与设计哲学。 提示词 请使用 Emoji 风格编辑以下段落&#xff0c;该风格以引人入胜的标题、每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。使用案例&#xff08;春日穿搭&#xff09; &#x1f338; 2025春季穿搭灵…...

C语言 ——— 认识C语言

认识 main 函数 main 函数是程序的入口&#xff0c;程序执行时会从 main 函数的第一行开始执行&#xff0c;且一个工程中 main 函数有且只有一个 标准的 main 函数格式&#xff1a; int main() {return 0; } int 是类型&#xff0c;这里指的是 main 函数的返回类型 return…...

44、Spring Boot 详细讲义(一)

Spring Boot 详细讲义 目录 Spring Boot 简介Spring Boot 快速入门Spring Boot 核心功能Spring Boot 技术栈与集成Spring Boot 高级主题Spring Boot 项目实战Spring Boot 最佳实践总结 一、Spring Boot 简介 1. Spring Boot 概念和核心特点 1.1、什么是 Spring Boot&#…...

STM32硬件IIC+DMA驱动OLED显示——释放CPU资源,提升实时性

目录 前言 一、软件IIC与硬件IIC 1、软件IIC 2、硬件IIC 二、STM32CubeMX配置KEIL配置 三、OLED驱动示例 1、0.96寸OLED 2、OLED驱动程序 3、运用示例 4、效果展示 总结 前言 0.96寸OLED屏是一个很常见的显示模块&#xff0c;其驱动方式在用采IIC通讯时&#xff0c;常用软件IIC…...

Android 中绕过hwbinder 实现跨模块对audio 的HAL调用

需求 Audio 模块中专门为 TV 产品添加了一些代码&#xff0c;需要在 hdmi 的 HAL 代码中进行调用以完成某些功能。 解决方法 首先将 hdmi HAL 要调用的 audio 接口函数所在的 .so 链接到最基本的 lib.primay.amlogic.so 中&#xff08;其它平台上这个 .so 文件的名字也可能是…...

基于单片机技术的手持式酒精检测电路设计

基于STC89C52单片机的酒精检测仪设计 目录 基于STC89C52单片机的酒精检测仪设计一、简介二、酒精测试仪总体方案设计2.1 酒精检测仪设计要求分析2.2 设计框图 三、硬件设计3.1 酒精检测电路3.2 模数转换电路3.3 STC89c52单片机电路3.4 LED显示电路3.5 声光报警电路3.6 按键和复…...

【车道线检测(0)】卷首语

车道线检测领域&#xff0c;早期的LaneNet、CondLaneNet等模型。现在在精度、实时性、复杂场景适应性等方面有了更多进展。 ​Head&#xff08;输出头&#xff09;的设计角度分类 在车道线检测任务中&#xff0c;Head&#xff08;输出头&#xff09;的设计角度直接影响模型的…...

记一次某网络安全比赛三阶段webserver应急响应解题过程

0X01 任务说明 0X02 靶机介绍 Webserver&#xff08;Web服务器&#xff09;是一种软件或硬件设备&#xff0c;用于接收、处理并响应来自客户端&#xff08;如浏览器&#xff09;的HTTP请求&#xff0c;提供网页、图片、视频等静态或动态内容&#xff0c;是互联网基础设施的核心…...

AI 越狱技术剖析:原理、影响与防范

一、AI 越狱技术概述 AI 越狱是指通过特定技术手段&#xff0c;绕过人工智能模型&#xff08;尤其是大型语言模型&#xff09;的安全防护机制&#xff0c;使其生成通常被禁止的内容。这种行为类似于传统计算机系统中的“越狱”&#xff0c;旨在突破模型的限制&#xff0c;以实…...

项目进度延误的十大原因及应对方案

项目进度延误主要源于以下十大原因&#xff1a;目标不明确、需求频繁变更、资源配置不足或不合理、沟通不畅、风险管理不足、缺乏有效的项目监控、技术难题未及时解决、团队协作效率低下、决策链过长、外部因素影响。其中&#xff0c;需求频繁变更是导致延误的关键因素之一&…...

瑞友客户端登录GS_ERP时,报错: 由于安全许可证服务器不能提供许可证,连接被中断的解决方法

瑞友客户端登录GS_ERP时&#xff0c;报错&#xff1a;由于安全许可证服务器不能提供许可证&#xff0c;连接被中断的解决方法 瑞友客户端登录GS_ERP时&#xff0c; 报错&#xff1a;由于安全许可证服务器不能提供许可证&#xff0c;连接被中断的解决方法是由于远程桌面连接协议…...

android wifi通过命令行打开2.4G热点

android系统支持2G和5G&#xff0c;但车机系统应用只支持5G&#xff0c;但是需要测试2.4G的射频 方法如下&#xff1a; 1、adb shell 进去&#xff0c;su 指定root权限&#xff0c;确保热点处于关闭状态 2、开启热点为www99999, 密码为12345678&#xff0c; wpa2的加密协议 cm…...

truncate,drop,delete分析

truncate,drop,delete对比分析 特性 TRUNCATE DROP DELETE **操作对象** 表中的所有数据 整个表及其所有数据 表中的特定数据 **是否保留表结构** 是 否 是 **是否可恢复** 不可恢复 不可恢复 可恢复 **性能** 高 高 低&#xff08;逐行删除&#xff09; …...

vue+flask图书知识图谱推荐系统

文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站&#xff0c;有好处&#xff01; 编号: F025 架构: vueflaskneo4jmysql 亮点&#xff1a;协同过滤推荐算法知识图谱可视化 支持爬取图书数据&#xff0c;数据超过万条&am…...

什么是微前端?有什么好处?有哪一些方案?

微前端&#xff08;Micro Frontends&#xff09; 微前端是一种架构理念&#xff0c;借鉴了微服务的思想&#xff0c;将一个大型的前端应用拆分为多个独立、自治的子应用&#xff0c;每个子应用可以由不同团队、使用不同技术栈独立开发和部署&#xff0c;最终聚合为一个整体产品…...

prompts提示词经典模板

prompts.py 中的提示词模板详解 文件中定义了两个核心提示词模板&#xff1a;REASON_PROMPT 和 RELEVANT_EXTRACTION_PROMPT。这两个模板在 DeepResearcher 的推理过程中扮演着关键角色。下面我将详细解析这两个模板的结构和功能。 REASON_PROMPT 详解 REASON_PROMPT 是用于指…...

pytorch软件封装

封装代码&#xff0c;通过传入文件名&#xff0c;即可输出类别信息 上一章节&#xff0c;我们做了关于动物图像的分类&#xff0c;接下来我们把程序封装&#xff0c;然后进行预测。 单张图片的predict文件 predict.py 按着路径&#xff0c;导入单张图片做预测from torchvis…...

DAY02:【pytorch】计算图与动态图机制

一、引言 在深度学习框架中&#xff0c;计算图是理解自动求导和模型优化的核心概念。无论是PyTorch的动态图机制&#xff0c;还是TensorFlow早期的静态图模式&#xff0c;计算图都扮演着关键角色。本文将深入解析计算图的基本原理&#xff0c;并结合PyTorch代码演示动态图的运…...

基于元学习(Meta-Learning)的恶意流量检测

元学习(Meta-Learning),即“学会学习”(Learning to Learn),通过让模型从多个相关任务中提取通用知识,实现对新任务的快速适应。在恶意流量检测领域,元学习的核心价值在于从少量攻击样本中泛化出检测规则,尤其适用于新型攻击、定向APT攻击等数据稀缺场景。 一、元学习…...

JavaScript:游戏开发的利器

在近年来的科技迅速发展中&#xff0c;JavaScript 已逐渐成为游戏开发领域中最受欢迎的编程语言之一。它的跨平台特性、广泛的社区支持、丰富的库和框架使得开发者能够快速、有效地创建各种类型的游戏。本文将深入探讨 JavaScript 在游戏开发中的优势。 一、跨平台支持 JavaSc…...

玩转Docker | 使用Docker搭建pinry图片展示系统

玩转Docker | 使用Docker搭建pinry图片展示系统 前言一、Pinry介绍Pinry 简介Pinry 特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署pinry服务下载镜像创建容器检查容器状态检查服务端口安全设置四、访问pinry应用五、pinry基本使用5.1 注册pinry账号…...

DAPP实战篇:使用web3.js实现前端输入钱包地址查询该地址的USDT余额——前端篇

专栏:区块链入门到放弃查看目录-CSDN博客文章浏览阅读385次。为了方便查看将本专栏的所有内容列出目录,按照顺序查看即可。后续也会在此规划一下后续内容,因此如果遇到不能点击的,代表还没有更新。声明:文中所出观点大多数源于笔者多年开发经验所总结,如果你想要知道区块…...

企业信息化-系统架构师(九十八)

1在对一个软件系统进行设计与确认后&#xff0c;需要进行架构复审&#xff0c;架构复审目的标识潜在风险&#xff0c;及早发现架构的缺陷和漏洞&#xff0c;在架构复审过程中&#xff0c;主要由&#xff08;&#xff09;决定架构满足需求&#xff0c;质量需求是否在设计中体现。…...

【C++算法】54.链表_合并 K 个升序链表

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a; 题目链接&#xff1a; 23. 合并 K 个升序链表 题目描述&#xff1a; 解法 解法一&#xff1a;暴力解法 每个链表的平均长度为n&#xff0c;有k个链表&#xff0c;时间复杂度O(nk^2) 合并两个有序…...

阿里云CDN应对DDoS攻击策略

阿里云CDN遭遇DDoS攻击时&#xff0c;可通过以下综合措施进行应对&#xff0c;保障服务的稳定性和可用性&#xff1a; 1. 启用阿里云DDoS防护服务 阿里云提供专业的DDoS防护服务&#xff0c;通过流量清洗中心过滤恶意流量&#xff0c;确保合法请求正常传输。该服务支持按需选…...

MySQL8的索引跳跃扫描原理

#MySQL 8 的索引跳跃扫描&#xff08;Index Skip Scan&#xff09;原理 1. 什么是索引跳跃扫描&#xff1f;索引跳跃扫描&#xff08;Index Skip Scan&#xff09;是 MySQL 8.0.13 引入的一种优化技术&#xff0c;允许在某些情况下跳过联合索引的最左前缀字段&#xff0c;仍然…...

centos 启动nginx 服务器

✅ 如果你是通过 yum 安装的 Nginx&#xff08;推荐方式&#xff09;&#xff1a; &#x1f539; 启动 Nginx&#xff1a; sudo systemctl start nginx &#x1f539; 设置开机自启&#xff08;建议开启&#xff09;&#xff1a; sudo systemctl enable nginx &#x1f53…...

格式化输出

% 符号相关 数据类型代码 %s&#xff1a;字符串 示例&#xff1a;print("名字是 %s" % "Tom") → 名字是 Tom%c&#xff1a;字符/ASCII码 示例&#xff1a;print("%c" % 65) → A%d/%i&#xff1a;有符号整数 示例&#xff1a;print("年龄…...

[leetcode]动态规划:斐波那契数列

一.线性dp 1.0什么是线性dp 线性DP就是指状态的转移具有线性递推关系&#xff0c;每个状态只依赖之前的状态&#xff0c;按照线性顺序一步步递推下去。 1.1斐波那契数列问题 #include <iostream> #include <vector> using namespace std; int main() { in…...

HackMyVM - todd记录

HackMyVM - toddhttps://mp.weixin.qq.com/s/E_-hepdfY-0veilL1fl2QA...

【spark认任务提交】配置优先级顺序

配置优先级顺序 Spark-submit 命令行参数 (最高优先级)代码中通过 SparkConf 设置的参数 (在应用程序中直接设置)spark-defaults.conf 文件中的配置 实际应用中的建议 固定配置&#xff1a;将集群级别的默认配置放在 spark-defaults.conf 中应用特定配置&#xff1a;将应用特…...

如何建立高效的会议机制

建立高效的会议机制需做到&#xff1a;明确会议目标、制定并提前分发议程、控制会议时长、确保有效沟通与反馈、及时跟进执行情况。其中&#xff0c;明确会议目标是核心关键&#xff0c;它直接决定了会议的方向与效率。只有明确目标&#xff0c;会议才不会偏离初衷&#xff0c;…...

spark Core-RDD转换算子

1. map算子&#xff1a;对RDD中的数据逐条进行映射转换&#xff0c;可实现类型或值的转换。函数签名为 def map[U: ClassTag](f: T > U): RDD[U] 。 2. mapPartitions算子&#xff1a;以分区为单位处理数据&#xff0c;可进行任意处理。与 map 相比&#xff0c; map 是分区内…...

【图像处理】C++实现通用Raw图转Unpack14的高效方法

一、需求背景 在图像处理领域&#xff0c;我们经常需要处理各种位深的原始数据&#xff08;如Raw8、Unpack10等&#xff09;。某些高端相机或传感器会输出14位精度的图像数据&#xff0c;但受传输限制&#xff0c;实际存储时可能采用低位深打包。本文将实现一个通用转换函数&a…...

Vue3的Composition API与React Hooks有什么异同?

Vue3的一个重大更新点就是支持Composition API&#xff0c;而且也被业界称为hooks&#xff0c;那么Vue3的“Hooks”与React的Hooks有这么区别呢&#xff1f; 一、核心相似点 1. 逻辑复用与代码组织 都解决了传统类组件或选项式 API 中逻辑分散的问题&#xff0c;允许将相关逻…...

Gerrit的安装与使用说明(Ubuntu)

#本页面按192.168.60.148服务器举例进行安装配置 1.权限配置 ## 使用root或者有sudo权限用户执行 # 创建gerrit用户 sudo useradd gerrit # 设置gerrit用户的密码 sudo passwd gerrit # 增加sudo权限 sudo visudo 在root ALL(ALL:ALL) ALL行下添加如下内容 gerrit ALL(ALL:…...

如何在Git历史中抹掉中文信息并翻译成英文

如何在Git历史中抹掉中文信息并翻译成英文 在软件开发和版本控制领域&#xff0c;维护一个清晰、一致的代码历史记录是至关重要的。然而&#xff0c;有时我们可能会遇到需要修改历史提交的情况&#xff0c;比如删除敏感信息或修正错误。本文将详细探讨如何在Git历史中抹掉中文…...