首页 文章 精选 留言 我的

精选列表

搜索[python],共10000篇文章
优秀的个人博客,低调大师

【Python量化实战入门 #02】宣传页的夏普、最大回撤,用麦蕊 K 线自己算一遍

## 一、为什么写这篇 你买基金、看策略,是不是常被一串指标砸脸:「年化夏普 2.3」「最大回撤仅 8%」? 这些数字到底是算出来的,还是写出来的?**很多人信了一辈子,却从没自己算过一遍。** 这一篇,我们拿麦蕊的**真实历史 K 线**,把累计收益、最大回撤、年化波动、夏普、胜率这五个「宣传页常客」亲手算一遍。算完你就明白:指标不神秘,神秘的是「它用哪段数据算的」。 ## 二、读完你能得到什么 - 用 `stock_history` 拉取任意股票的历史日 K 线; - 手写最大回撤(不用任何「神器」库); - 搞懂夏普比率到底在比什么; - 一个可复用的 `main.py`,改个代码就能算任意标的。 ## 三、环境准备 ## 四、核心代码 ```javascript import os, sys, datetime from mairui import Client import math def get_close_series(mc, code, days=120): end = datetime.date.today().strftime("%Y%m%d") start = (datetime.date.today() - datetime.timedelta(days=days)).strftime("%Y%m%d") rows = mc.stock_history(code, "d", "n", st=start, et=end) # dividend='n' 不复权 clean = [r for r in rows if r.get("c") is not None and r.get("t")] clean.sort(key=lambda r: r["t"]) return [r["c"] for r in clean] def max_drawdown(close): peak = close[0] mdd = 0.0 for p in close: if p > peak: peak = p dd = (p - peak) / peak if dd 0 else 0.0 win = sum(1 for r in rets if r > 0) / n return cum, mdd, ann_vol, sharpe, win mc = Client("LICENCE-66D8-9F96-0C7F0FBCD073") # 演示证书,演示证书(读者复制即用,无需替换) close = get_close_series(mc, "000001", days=120) cum, mdd, ann_vol, sharpe, win = stats(close) print(f"累计收益率 {cum*100:.2f}% 最大回撤 {mdd*100:.2f}%") print(f"年化波动率 {ann_vol*100:.2f}% 夏普(简化) {sharpe:.2f} 胜率 {win*100:.2f}%") ``` ## 五、跑出来的真实结果 本机当天实跑(非虚构),以平安银行(000001) 近 120 日窗口: ```javascript === 000001 近 82 个交易日 === 首个交易日: 2026-04-08 收盘 11.22 最后交易日: 2026-08-05 收盘 11.25 --- 手算收益风险指标 --- 累计收益率 : 0.27 % 最大回撤 : -12.76 % (从山顶到谷底最狠的一刀) 年化波动率 : 18.04 % 夏普(简化) : 0.14 胜率 : 46.91 % (38/81 个交易日上涨) ``` ## 六、原理拆解(口语化) - **累计收益率**:首尾一减一除,最直白。 - **最大回撤**:想象你账户净值画成一条线,**从任意「山顶」跌到之后「谷底」最狠的那一刀**,就是最大回撤。它不管你最后赚没赚,只记你「最痛的一次」。代码里边走边记历史最高 `peak`,每步算 `(当前-peak)/peak`,取最小。 - **年化波动率**:日收益上下乱跳的幅度 × √252(一年约 252 个交易日)。跳得越凶,波动越大。 - **夏普比率(简化版)**:`年化收益 ÷ 年化波动`。它回答一个问题——**你每多冒一份险,多赚了几份钱?** 这里无风险利率取 0,所以夏普偏低;真实算法要减掉国债收益。 - **胜率**:上涨交易日 ÷ 总交易日。46.91% 说明这只票近 4 个月「涨的日子还没跌的多」,但累计却微涨 0.27%——因为涨的日子平均涨得比跌的日子狠。**这就是胜率低不等于亏钱的例子**。 > 一句记忆:**最大回撤量「最痛」,夏普量「值不值」,胜率量「频率」**,三个维度拼起来才看懂一个策略。 ## 七、常见坑 1. **复权参数填错**:`stock_history` 的 `dividend` 用 `'n'`(不复权)或 `'q'`(前复权),填 `'1'` 会直接返回 `{'error':'数据不存在'}`。 1. **返回是 list 不是 dict**:别拿 `hist['data']` 去取,直接遍历。 1. **空行**:历史接口偶尔返回 `c` 为 `None` 的行,计算前务必过滤,否则除零崩。 1. **夏普别神化**:简化版没减无风险利率,且对「用哪段窗口算」极度敏感——换段时间数字能差很远。 ## 八、小结 + 下篇预告 今天你亲手算出了夏普和最大回撤。下次再看到「夏普 2.3」的宣传,你会先问:**这段 K 线从哪天到哪天?** 下一篇 **#03**:把行情落进 **SQLite**,搭一个本地行情库,让历史 K 线随用随取、还能增量更新(也呼应盯盘系列的落库那篇)。 ⚠️ 本文仅为技术实现演示,**不构成任何投资建议**。指标计算依赖历史数据,不代表未来表现;市场有风险,投资须谨慎。代码示例仅用于学习。

优秀的个人博客,低调大师

[python作业AI毕业设计博客]Java Programming 9th Edition - 2019 java程序设计第9版 下载

Farrell的JAVA PROGRAMMING,9E中有引人入胜的实际操作方法时,会发现Java(TM)在开发应用程序方面的强大功能。 即使您是第一次使用,JAVA PROGRAMMING也可以向您展示如何快速开始开发有用的程序,同时仍然掌握结构化和面向对象编程的基本原理。 独特的,易于阅读的解释和有意义的编程练习强调业务应用程序和游戏创建,同时有用的调试练习和当代案例问题进一步扩展了您的理解。 MindTap中的其他数字学习资源提供交互式学习工具以及编码IDE(集成开发环境)实验室,用于练习和扩展您的技能。 下载 https://itbooks.pipipan.com/fs/18113597-335627109 第九版,介绍Java编程概念, 强制执行良好的风格,逻辑思维和面向对象的范例。从一开始就覆盖对象,比许多其他教科书更早。您将在第1章中创建第一个Java程序。第2章,第3章和第4章将增加您对面向对象环境中数据,类,对象和方法如何交互的理解。 第5章和第6章探讨了输入和重复结构,它们是编程逻辑的支柱,对于用任何语言创建有用的程序至关重要。您将在第7章,第8章和第9章中学习字符串和数组操作的特殊注意事项。 第10章,第11章和第12章彻底介绍了继承和异常处理。继承是面向对象的概念,它允许您通过调整现有对象的特征来快速开发新对象;异常处理是面向对象的处理错误的方法。两者都是面向对象设计中的重要概念。第13章提供了有关处理文件的信息,以便您可以存储和检索程序输出。 第14章介绍了GUI Swing组件,这些组件用于创建视觉上令人愉悦的,用户友好的交互式应用程序。 第15章介绍了JavaFX,它是为桌面和Internet创建和交付应用程序的最新平台。第15章由Sandra Lavallee撰写,Sandra Lavallee是新罕布什尔州拉科尼亚湖区社区学院的教授和计算机与设计技术部主席。

优秀的个人博客,低调大师

Python识别验证码!学会这步,百分之60的网站你基本都能识别了!

识别原理 我们采取一种有监督式学习的方法来识别验证码,包含以下几个步骤 图片处理 - 对图片进行降噪、二值化处理 切割图片 - 将图片切割成单个字符并保存 人工标注 - 对切割的字符图片进行人工标注,作为训练集 训练数据 - 用KNN算法训练数据 检测结果 - 用上一步的训练结果识别新的验证码 下面我们来逐一介绍一下每一步的过程,并给出具体的代码实现。 127是我们设定的阈值,像素值大于127被置成了0,小于127的被置成了255。处理后的图片变成了这样 接下来,我们应用高斯模糊对图片进行降噪。高斯模糊的本质是用高斯核和图像做卷积,代码如下 kernel = 1/16*np.array([[1,2,1], [2,4,2], [1,2,1]]) im_blur = cv2.filter2D(im_inv,-1,kernel) 降噪后的图片如下 可以看到一些颗粒化的噪声被平滑掉了。 降噪后,我们对图片再做一轮二值化处理 ret, im_res = cv2.threshold(im_blur,127,255,cv2.THRESH_BINARY) 现在图片变成了这样 好了,接下来,我们要开始切割图片了。 切割图片 这一步是所有步骤里最复杂的一步。我们的目标是把最开始的图片切割成单个字符,并把每个字符保存成如下的灰度图 首先我们用opencv的findContours来提取轮廓 im2, contours, hierarchy = cv2.findContours(im_res, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) 我们把提取的轮廓用矩形框起来,画出来是这样的 可以看到,每个字符都被检测出来了。 但这只是理想情况,很多时候,相邻字符有粘连的会被识别成同一个字符,比如像下面的情况 要处理这种情况,我们就要对上面的图片做进一步的分割。字符粘连会有下面几种情况,我们逐一来看下该怎么处理。 4个字符被识别成3个字符 这种情况,对粘连的字符轮廓,从中间进行分割,代码如下 result = [] for contour in contours: x, y, w, h = cv2.boundingRect(contour) if w == w_max: # w_max是所有contonur的宽度中最宽的值 box_left = np.int0([[x,y], [x+w/2,y], [x+w/2,y+h], [x,y+h]]) box_right = np.int0([[x+w/2,y], [x+w,y], [x+w,y+h], [x+w/2,y+h]]) result.append(box_left) result.append(box_right) else: box = np.int0([[x,y], [x+w,y], [x+w,y+h], [x,y+h]]) result.append(box) 分割后,图片变成了这样 4个字符被识别成2个字符 4个字符被识别成2个字符有下面两种情况 对第一种情况,对于左右两个轮廓,从中间分割即可。对第二种情况,将包含了3个字符的轮廓在水平方向上三等分。具体代码如下 result = [] for contour in contours: x, y, w, h = cv2.boundingRect(contour) if w == w_max and w_max >= w_min * 2: # 如果两个轮廓一个是另一个的宽度的2倍以上,我们认为这个轮廓就是包含3个字符的轮廓 box_left = np.int0([[x,y], [x+w/3,y], [x+w/3,y+h], [x,y+h]]) box_mid = np.int0([[x+w/3,y], [x+w*2/3,y], [x+w*2/3,y+h], [x+w/3,y+h]]) box_right = np.int0([[x+w*2/3,y], [x+w,y], [x+w,y+h], [x+w*2/3,y+h]]) result.append(box_left) result.append(box_mid) result.append(box_right) elif w_max < w_min * 2: # 如果两个轮廓,较宽的宽度小于较窄的2倍,我们认为这是两个包含2个字符的轮廓 box_left = np.int0([[x,y], [x+w/2,y], [x+w/2,y+h], [x,y+h]]) box_right = np.int0([[x+w/2,y], [x+w,y], [x+w,y+h], [x+w/2,y+h]]) result.append(box_left) result.append(box_right) else: box = np.int0([[x,y], [x+w,y], [x+w,y+h], [x,y+h]]) result.append(box) 分割后的图片如下 4个字符被识别成1个字符 这种情况对轮廓在水平方向上做4等分即可,代码如下 result = [] contour = contours[0] x, y, w, h = cv2.boundingRect(contour) box0 = np.int0([[x,y], [x+w/4,y], [x+w/4,y+h], [x,y+h]]) box1 = np.int0([[x+w/4,y], [x+w*2/4,y], [x+w*2/4,y+h], [x+w/4,y+h]]) box2 = np.int0([[x+w*2/4,y], [x+w*3/4,y], [x+w*3/4,y+h], [x+w*2/4,y+h]]) box3 = np.int0([[x+w*3/4,y], [x+w,y], [x+w,y+h], [x+w*3/4,y+h]]) result.extend([box0, box1, box2, box3]) 分割后的图片如下 对图片分割完成后,我们将分割后的单个字符的图片存成不同的图片文件,以便下一步做人工标注。存取字符图片的代码如下 for box in result: cv2.drawContours(im, [box], 0, (0,0,255),2) roi = im_res[box[0][1]:box[3][1], box[0][0]:box[1][0]] roistd = cv2.resize(roi, (30, 30)) # 将字符图片统一调整为30x30的图片大小 timestamp = int(time.time() * 1e6) # 为防止文件重名,使用时间戳命名文件名 filename = "{}.jpg".format(timestamp) filepath = os.path.join("char", filename) cv2.imwrite(filepath, roistd) 字符图片保存在名为char的目录下面,这个目录里的文件大致是长这样的(文件名用时间戳命名,确保不会重名) 接下来,我们开始标注数据。 人工标注 这一步是所有步骤里最耗费体力的一步了。为节省时间,我们在程序里依次打开char目录中的每张图片,键盘输入字符名,程序读取键盘输入并将字符名保存在文件名里。代码如下 files = os.listdir("char") for filename in files: filename_ts = filename.split(".")[0] patt = "label/{}_*".format(filename_ts) saved_num = len(glob.glob(patt)) if saved_num == 1: print("{} done".format(patt)) continue filepath = os.path.join("char", filename) im = cv2.imread(filepath) cv2.imshow("image", im) key = cv2.waitKey(0) if key == 27: sys.exit() if key == 13: continue char = chr(key) filename_ts = filename.split(".")[0] outfile = "{}_{}.jpg".format(filename_ts, char) outpath = os.path.join("label", outfile) cv2.imwrite(outpath, im) 这里一共标注了大概800张字符图片,标注的结果存在名为label的目录下,目录下的文件是这样的(文件名由原文件名+标注名组成) 接下来,我们开始训练数据。 训练数据 首先,我们从label目录中加载已标注的数据 filenames = os.listdir("label") samples = np.empty((0, 900)) labels = [] for filename in filenames: filepath = os.path.join("label", filename) label = filename.split(".")[0].split("_")[-1] labels.append(label) im = cv2.imread(filepath, cv2.IMREAD_GRAYSCALE) sample = im.reshape((1, 900)).astype(np.float32) samples = np.append(samples, sample, 0) samples = samples.astype(np.float32) unique_labels = list(set(labels)) unique_ids = list(range(len(unique_labels))) label_id_map = dict(zip(unique_labels, unique_ids)) id_label_map = dict(zip(unique_ids, unique_labels)) label_ids = list(map(lambda x: label_id_map[x], labels)) label_ids = np.array(label_ids).reshape((-1, 1)).astype(np.float32) 接下来,训练我们的模型 model = cv2.ml.KNearest_create() model.train(samples, cv2.ml.ROW_SAMPLE, label_ids) 训练完,我们用这个模型来识别一下新的验证码。 检测结果 下面是我们要识别的验证码 对于每一个要识别的验证码,我们都需要对图片做降噪、二值化、分割的处理(代码和上面的一样,这里不再重复)。假设处理后的图片存在变量im_res中,分割后的字符的轮廓信息存在变量boxes中,识别验证码的代码如下 for box in boxes: roi = im_res[box[0][1]:box[3][1], box[0][0]:box[1][0]] roistd = cv2.resize(roi, (30, 30)) sample = roistd.reshape((1, 900)).astype(np.float32) ret, results, neighbours, distances = model.findNearest(sample, k = 3) label_id = int(results[0,0]) label = id_label_map[label_id] print(label) 运行上面的代码,可以看到程序输出 y y 4 e 图片中的验证码被成功地识别出来。 我们测试了下识别的准确率,取100张验证码图片(存在test目录下)进行识别,识别的准确率约为82%。看到有人说用神经网络识别验证码,准确率可以达到90%以上,下次有机会可以尝试一下。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册