---
title: '用python写A股量化交易系统 17 完成回测'
source: 'https://www.youtube.com/watch?v=LwlYP0WNhb4'
video_id: 'LwlYP0WNhb4'
date: 2026-09-15
duration_sec: 2366
channel: 'rickey说量化'
---

# 用python写A股量化交易系统 17 完成回测

> Source: [用python写A股量化交易系统 17 完成回测](https://www.youtube.com/watch?v=LwlYP0WNhb4)

## Summary

The video is a continuation of a backtesting tutorial, where the creator walks through using tick data to run a backtest. The focus is on converting tick data into a usable format, building an order list, and calculating P&L with fees and taxes. The session includes parameter adjustments and a review of the results.

### Key Points

- **Tick Data Caching** [00:13] — The logic checks if tick data exists; if not, it creates it every 5 minutes.
- **DataFrame Conversion** [02:02] — Tick data is a DataFrame; it needs conversion into a list of tuples with datetime and price.
- **Building Order List** [03:47] — The order list contains tuples with symbol and price, using the 'Key' that was already used.
- **P&L Calculation** [32:31] — P&L is calculated as (close - open) * volume, minus stamp duty (0.1%) and commission (0.03%).
- **Parameter Adjustment** [34:38] — Parameters are adjusted from 5% to 4% to improve results, but the backtest still shows losses.
- **Buy/Sell Thresholds** [34:50] — The strategy buys at 0.96x MA20 and sells at 1.04x MA20.
- **Trade Results** [36:51] — The backtest ran 19 trades with mixed results; some profitable, some losing.
- **Subjectivity in Parameters** [37:42] — The creator notes that quant parameters are subjective and questions how to validate them.

## Transcript

这次我们来把回测做完 上次我们是写到了这个数据这里 这个Tick for Bad Testing这里
现在我们就要把这个数据用来 用这个Tick数据用来回测 这里的话我们的逻辑是get到这个tick数据
如果它存在我们就直接调用 如果没有我们就五分钟来创建这个tick数据
这个Tick数据还要 这里是个DataFrame的格式 所以我们还要把它再稍微转一下,再转化一下
因为我们在之前的 Tick这里呢,是一个Tap来的 这里要改一下
这里改成这个,放到前面来 显示时间跟价格,导致价格跟时间 那我们这里下面的这个Jetting
就要再做一些转化,对不对?我们就来先做转化 这个,这个,这个 然后这个Tick Pass
是这一个 它对应该是存在的啦 它这反而是反而一个true 反而一个true的话呢 就直接读这个 读到这里呢
这个kickdata呢 它就是一个dataframe的格式来的 谢谢 它是一个
它是一个dataframe的一个格式 我们又把它变成一个类似一个 然后里面有一个一个的tuple
包含着date time跟这个price 现在怎么转呢 怎么转呢 Rendex Growth
Win Ticks Dictor 我先看看他那个是到底什么样的游走 这肉啊,这样子是这样
他是一个 加个影 这也太巧了,加个head
我们用死亡来参加 嗯 好像就对了 是不是 他有一个date time 有一个price 然后他这个date time
就是我们之前设计过了 他每一个dip都会加一个0.1秒 好 那这个就是没问题了 问题的话呢 去掉
给他搞个名称 下面一个AR对不对 拆个名字
AR0号还是用名称 KeyX已经用过了 我们这里把它append进来
append 加一个括号 这样的话我们的这个
这个kick list就应该 就这样子 有个TenElements,然后是Tuple 每个Tuple都是一个对称和一个Price
把全部给找进来 我们的txt就搞定了,搞定了这个txt它有8万条数据,7954
然后呢 然后我们这里是 这中是return一个txt 我们这里要改一下 下面是arrow
是num排的arrow 就再快一下 我们数据格式重新一下 就txt txt.input.arrow
我们来一回 讲 tick 是一个list
去一下 那我把这个tick列成 areaOfObject
讲 要按 这个有tick 这样我们用了一个中间的变量,叫ticklist 然后把这个data prime里面的数据都取出来,append到这个list
然后最终把这个list变成这个numPy array 其实用list也可以,用这个list也是可以的 只是因为我们下面用的这个array
都是numPy的这种array的数据,所以这里 我想同一点应该会更好一点 所以我们这里把它变成numPy array 然后复给这个txt 那一开始这个txt呢 这个bottom frame到这里
这个txt就变成这个numPyArea了 然后再return出来 再return出来 return出来 那这样的话呢 我们就已经得到这个txt
那这个就least 就不用了 这个算了 remove tickleast有了 tickleast有了 还是个 error object 先不理他
那我们这里这个就写完了 写完的话呢我们就要把它 就放到里面来 放到里面来
You have ticked out the music or created ticked out the file the bar data, try ticked out and save it 这里有了
这个就磨到最上面 那我们现在就用这个ticks来跑我们这个 这个backtesting 那我们的backtesting呢
上次我们写到这个strategies这里就写完了 有个sell 有个buy 有个bar generator 这个是for
这个是for curb trading 这个是for for real trading 这个不是用于这个
不是用于这个backtracking cdick cdick Bedtip这个也是一样的
我们这里就来写Bedtesting的代码
这个常识 那backtesting呢 它需要用到什么东西呢 我们首先 得把这个ticks 传给这个backtesting
对不对 然后呢这个backtesting去调每个行数 应该是这样子 做
ticks in ticks 然后再去调这个self.bar generator 那这里就需要传一个text出来
然后呢,这个bar generator走完呢 就创建好了加个Tick
这里加个Tick 我这里已经有Tick在上面了 这个Tick在这里
这个是给Vue 给Pay per trading或者是Real trading 我们写了一个
其实也可以在这里去加一个tick 然后去区分一下 就是说他每次我们给用这个bar generator的方式区分一下 现在是Pay per trading Real trading或者是一个Bet testing
但是那样子的话 他需要每一个tick 他去做一个判断 其实会有点 会影响效率 我们可以专门写一个 上面写一个来给这个testing
body for testing 然后传给他一个txt 对不对 那我们就 update-boss-cooker-5-minute-ma
这个就去吧 那这边的这些txt呢 就通通就不用去掉那个 就不去调那个 我们直接就调这个参数
是这样 看吧 这个没影响
这是非常简单的,这个是bar generator for testing 它最终也会生成一个open high low,这个也会生成open high low
是不是,有open high low,跟一个dt,还有告诉程序,这是不是一个new bar 所以这个其实我们这样写就把这个BarGenerator在实行交易或者是在回车的时候把它区分开了
我们先用这个C一下 去做搜索,back testing 好,它這個搞定的時候呢
isMubar就需要去掉那個fault.shred 就算吧,如果是mubar它就去算到MA 或者是看要不要開關
可是这里还存在一个问题 就是说 怎么说呢 我们在一开始交易的时候 在一开始计算的时候呢
我们现在是五分钟的
那 那比如一开始我们 我们现在这个数据是从 2020年的一月份开始的
不能调,没关系,过一下 因为一开始,分成了这些bar,这些open,high,low,close
从0开始,一开始是没有了,所以它是从0 它是先这里会 然后呢
然后就第二个 到 它就会一路创建 它会一路创建到一定的8之后呢
我们才能够它是把它创建20个嘛 创建20个我们才能 我们才能够来开始算这个MA嘛 因为我记得我们这个MA是要用20个
那你在一开始比如说我只有三四个8的时候 去算这个MA呢 其实就算出来那个数字就是错了 是不能用的 对不对 所以这里我们需要探浪一下
到底这个8的这个 就是这个self.open 这open也行 用close high low都可以
这个的话至少他要等于20个 他要等于20个
像我们用的 一般都会说给一个莫正的比率100 50 但这里呢 你问你这个数字是多少你不能够低于20个
对不对 所以你需要判断说我这个 已经创建的这个8 达不到达到你达不到你的那个开箱计算的要求了 所以自己就要再稍微的处理一下
才能够来调这个self.setup 你不能我们不想再有JP2.8的时候去调这个self.setup 怎么样这有什么写的
这样吗 这样写 试试
这样写也可以 只是这么写它 它会每一次都去算这个8 我们这里给它稍微改一下 So if soft.ignite
好了 初始化好了 我们就叫这个soft.12g 有意思吗 我们接着往下看 else
他就判断 else就判断
就是因为一开始这个是慢来的 我们这一个点击的设置为nice 这样子的话呢,它就是一开始是force来的
然后我们就去,因为你的bar生成好了嘛 它就去看到你,它一开始是force,就爬到这里来 然后他就看了你这个吧到底能达到你要的比如百克的目标
如果达到了他就给你把它改成这个处 那改成处之后呢 下一次的你这个吧创建好了
他就会给你 调用他就会对他因为自己是点处的 以处他处的时候他就会来调用这个设计 就是你就会进行你的开场计算 那
这样就可以了 可不可以 我现在一个一个吧的进来 我一个一个进来生成了第一个吧 第一个吧好了
然后呢 这里是force 这里是true 然后就进入到这里 判断一下是大于100 等到我有 new bar created 我的100个把充电好的这里
然后呢 我就100个 100个就是出来 出了之后呢 就还算了 可是我这里没有掉算机 我留到下一个
下一个 这里会存在一个问题 所以说 我们画个图
这里写个图 就讲明天星期
明天这里就 开盘 开完之后呢 他就给你第一个提过来的这个8 这个08就生成了 对不对 这个08就生成了
然后假设刚好到了这一个 我们这个是0 这是0嘛 对不对 然后这里是1嘛 然后1-2 我们假设刚好这里是达到了有100个吧
100个吧 100个吧 那我们 算上这一个是100个吧 那我们就是2了对不对这个就是触触了之后下一个踢进来k进来,它就比如说,继续涨一点点第二个tick进来这里第二个tick进来这里之后,那么它
它这回呢,这个ignite是触来了,是触,是针子来的 那它就调用这个strategy 调这个strategy,这时候它就来调这里 然后这个isNewBar是个
是个true还是false 因为它不是第一个tick,它是second tick嘛 那么在second tick的时候,它就已经不是第一个tick了 它会跑到这一段 因为第二个tick它是去update这个current tick
所以它就update current bar update current bar的话,它就会跑这几个代码 然后最后把这里isNewBar设成为false 设成了一个force
设成一个force的话呢 它这里呢就是一个force 一个force 一个force它就不进来 不进来它就会直接跑到这里来 跑到这里来呢就是它这里等于0
因为我们还没有开始 没有order嘛 所以这里是0是2 它就进来 进来之后呢它就会开始用这个close呢 跟这个MA20去算 可是这回这个MA20呢 它是一个
一个烂值 对不对 那么 你用 你这里是个增 这个是不是 这是一个数值嘛
比如说你40块 乘以 0.95 然后呢去跟
他会给你报错 这是unsupported operand fault 跟nontype
所以他是type error 所以你这里就会报错 报错的话呢 就是说你要么你有两个方式 或者说我有两个方式 第一个就是把这里改成0
改成0 改成0是个好主意吗 或者我们这里就改成0 一开始把它设置为不是None而是0
那就过了 那第二个就是说我 因为我们的这个MA只有在 只有在
只有在isTrue的时候 这个是true的时候呢 他才会去计算 他才会去去计算 对不对 是这里 所以我们这里呢
我们到这里的时候呢 其实如果 因为他刚好在这里会创建一个newbar 对不对 因为他这里是self.bar generator
创建了一个newbar 然后这里还是force 这里还是force 然后到这里判断了一下之后 发现他超过100 超过等于至少大于等于100个 他才会把这个变为2
那这回呢就是 就是还是第一个8的时候 对不对 第一个8的时候 这里时候就你就应该要计算了 如果你可以在这里加一个算
这回他就会去算 所以我达到100个8真的很算 可这样写的话呢 它就会 初始化成功 就开始调你的strategy 然后以后呢
都是会 因为你初始化已经成功了 它就会抖音了 就去调这个strategy 这样写的话呢 也可以避免刚刚那个 这个type error 我们用这个方法
因为一般我都不想设个零值 设个零值应该也是可以的 你也可以试一下 这里设个零值应该也是可以的 然后这里顺便改一下,因为这个Ease New Bar呢,我们把它改成这个
拖空写法 跟其他的一样 因为前面都是这个有一个拖空嘛,就是说这里
有个核心写法,我们这叫核心写法 这个用下滑线来连接你的这个word 找下一个伺服器
顺便改一下 所以在这里 runbacking 就可以叫你的strategy strategy我们ticks也进来了 我们来试一下,看能不能跑起来
我们这些先换一下 这个就挪到最上面来
我拿去上面来 我拿去上面来
我拿去上面来 什么东西
这是什么东西 这个不要了
现在说不定是什么重要的讯息 现在要过去
我們可以去看看Tix 我們可以去看看Tix
我們可以去看看Tix 我們可以去看看 run back testing run back testing
A-START TRADING 要上上去吗?这个 SHARED ENAMEL
我们找到MA之内 我们这里 MOVING OVER 然后呢 就要掉这个run by testing protection tick tock toe toe toe
ok ok t.run by testing 我们把这个tick转进来 来 好
好 我们来试一下 换个新的 如果你不想用这个 这个新的Console呢,最好就是把这些Variables都删掉
因为有时候这些Variables会造成一些意想不到的结果 我们试一下
嗯?不动了 the key object is not subscriptable 什麼鬼
這要把它改成一個list 為什麼改成list呢 我們就隨便弄一個給大家看會什麼 kernel.keys
以前我已經把它的keys調出來了 我记得掉他的0或者1,他发现有一个问题 我们有一个比如说 A-1
B-2 我们DSTT 他的keys
notifying 漏了个点 是不是,它已经有A跟B了
它是一个list嘛,那我们就直接去调它的名 它就告诉你,Dickies object is not subscriptable 就是说它这个还不是一个list
它这个还不是一个list,你需要把它再改一下 把它强制的改成一个Li 接着他 再接着他
我不需要排
玩吧OK跑完了 跑完了,我们来看一下他的ordered AST CurrentOrdered
是空的 是空的 有新的嗎
這裡有一單喔 他的Open Order One就開了一單 一年下來跑一單喔
2月3号开的一路到7月6号才关掉 是7月6号 然后他是在2月3号开仓的 9点半就开始一开完就买多
挣了真的多少钱 挣了4块钱 挣了4块钱 volume是三 三天果 对我们用的是十万块
10万块这个就差不多3000股的 然后是这个卖掉了 诶? Profit and Lost没有上 他这个没有帮我们上这个Profit and Lost 我们再一句
上这个Profit and Lost Profit and Lost 在哪里上呢 Profit 这里有个Sell是不是 我们有一个 给他的一个Code Size
给了一个Code Date Time 给他上一个 这个等于怎么算呢
这个要等于 等于这个close price 等于这个close price就是这个price
price 剪掉你的这个open price 这里没有open price,只能去掉了 小open price
是 open price,然后乘以 乘多乘以 乘以它的 volume
这个volume是手数的 还是 哦,这个是股数 那就直接升你这个volume就好了
对不对 我们这个这是我们的算的价差 这个是我们的数量 然后我们还要减掉那个
印花税 印花税 一花税什么是啊?一花税是什么? 是钱鸡,还是这个price
你要到你这个麦克贷的,他给你算一花税了,现在是这个price 一千分之一,这个是你的一花税 然后点到你的那个佣金,佣金是多少呢?
佣金,画面是3 万分之3,那就要这样 现在我们做 点掉
过来一点 不过不够写 因为它一码一码都要算嘛 对不对 现在讲不要算了 加号
加号再乘以你的 你的volume 看你的volume volume再乘以你的 volume是3
方便 这样应该就是你的P&L了 你这个坑就是你这个P&L 这个是你的佣金成本
这个是你的印花税 这里好像少了一个什么 这是你的volume volume不能这么写 因为没有volume 这个是你的印花税,价格,成品数量,你俩卖出的金额,成品成本之一,印花税,这个是你买一卖的佣金,这是你的价格成本税,那应该能讲
那我们这里再排,这点一带有点少了,我们再调节不要那么严苛,我们放松一点
我们不要打到5% 我们4%就好了 减掉4%就是这个 然后呢 减掉4% 这个是加速
对不对 这价格低1% 如果你的close 你的moving average 0.96 就是你的close 能到你的MA20的
0.96倍 然后涨到1.04倍 它就卖掉 我们再排一下
好像没变好 它这里给你上一个Panel 嗯? 它不挣钱了吗? 它可能过早就离场了
亏了六千块钱 好吧 我们再一单
我们把田仁再放上 完成 98到1.0了
这是上下的吧,那我们就进行交易了,再重新试一下 再弄弄弄,然后重新跑 看Odyssey
哎呦,这只有这么多吧?19 那也太多了 4900 这么多
第一单是挣了 5600 亏了4300 3挣了3300 亏了2400 挣了3800 2千块
挣了1500 亏了100块钱 现在2700可以1000块钱 可了1600可240
真的4400 2400 快300 可200 说了19单 总体来说 我这应该是挣钱的 我们就太多了 这不太多了
太多了 我们 再 也可以
看到没有?其实所谓的量化呢,其实也很多都是很主观的 比如说这个参数的设置 为什么是0.9,为什么是3%呢?对不对? 那其实,所以,那你有什么方式去确定你这个参数是好的呢?
这里有一张还没有停,按着关掉,order 5,order 5,那的话呢 它是45块买的,45块买了,那按照今天的,最近的收盘价来算
那也是挣钱的 也是挣钱的 但是这里只做了有4单 我看一下 快是9000块呀 挣了9500
那这个还好算挣了13000左右 挣了13000块左右 然后这个也挣的 这个也是挣的 这个挣了5块钱 按照最近的收盘价是多少钱
51 这个是45 5块钱 6块钱 那好 我们
就回客也跑起来跑完了 你没看到他已经把order都给你讨论我们看了 那是这样子呢因为 你看就知道是什么问题了 所以我们这个order是一个定档的程序
我们做了多少档 每档的盈利怎么样 或者是我们的定值区限呢都没有 我们下次再来 补上这个功能 今天就先讲这个 回社讲完就好了
那今天就这么多 小小一讲四十分钟 好各位拜拜
