2024 Chat GPT4 部署(不需要OpenAI账号,美国支付手段和每月20美金订阅)

订阅Copilot一段时间了,对于编程问题他能够很好回答,但是对于其他领域的问题,他基本上都会拒绝回答。Bing和OpenAI我都没有特别好的方法正常使用。无意中发现了OpenAI API 中转服务 + ChatGPT Next Web,能够正常使用ChatGPT4的图文聊天功能,直接解决了我的烦恼。

前提条件

我是按照 GPT-4 Turbo搭建教程 搭建,写得很详细。必须说明,这不是免费的,费用包括:中转服务收费(按Token收费)和服务器费用(20元/月)。

  1. 香港服务器(文章中推荐香草云,而我正好有其他云机器在香港,所以免却了很多麻烦。香草云我没有用过,看线路可以选择CN2 BGP线路,延迟上不会太差。我一直用的腾讯云香港的机器延迟基本120MS以上,今天ping一下居然变成了12MS,看来腾讯云还是调整了线路了。)
  2. OpenAI-HK (openai-hk.com)注册,充值。支持Chat GPT4,费用上按token收费,收费价格还可以接受(token就是你和AI对话的内容的字数)。ChatGPT 3.5 turbo价格比较便宜,GPT4 费用较高。

其实有了OpenAI 的API中转服务,就意味着你有了一个access token,可以随时访问OpenAI,除了文章中提及的使用ChatGPT Next Web网页以外,还可以自己写脚本调用,可以接入各种各样的使用场景。但目前GPT商店中提供的各种AI agent功能还木有。

部署ChatGPT Next Web

ChatGPT Next Web 是一个chatgpt的封装,通过API key访问openai的chatgpt获得结果。目前只能做文字交流。

部署上其实非常简单。先购买云服务器。推荐各个云服务器厂商香港的CVM或者轻量服务器,基本上1core2G足够了。但关注线路,不然卡得你怀疑人生。选择CentOS7镜像。

机器下来之后,首先加一下安全组,放开某个端口号允许访问。一般而言,如果你后面要挂域名,就得开80和443,不然后面按照IP:端口访问,这个端口号一般开20000后的,避免公网扫描。

登录机器。文章中说要用FinalShell,其实大可不必。现在云厂商一般都集成了Web端登录。比如腾讯云里,找到机器示例,等旁边的登录即可。这回我们用root登录。

接下来安装docker。

# 下载 docker安装脚本
curl -fsSL https://get.docker.com -o get-docker.sh
# 执行安装(root或者sudo吧)
sh get-docker.sh
# 启动docker
systemctl start docker
# 检查docker服务是否运行
systemctl status dockerCode language: Bash (bash)

我机器的yum源可能比较老旧了,所以安装的是卡在了nginx 的yum update上,报:Peer’s Certificate issuer is not recognized. 我手动关闭了ssl verify。

vi /etc/yum.conf
# 增加
sslverify=false
yum clean
yum repolistCode language: PHP (php)

后面docker安装好,就是一键部署ChatGPT Next Web

docker run  --name chatgpt-next-web -d -p 你的端口:3000 \
-e OPENAI_API_KEY=你的中转key \
-e CODE=页面访问密码 \
-e HIDE_USER_API_KEY=1 \
-e BASE_URL=https://twapi.openai-hk.com yidadaa/chatgpt-next-webCode language: JavaScript (javascript)

替换上述的 你的端口,你的中转key,页面访问密码。顺利执行,如果没有什么报错,那么你的网页就已经起来了。

最后在浏览器输入 你的外网IP:你的端口:

部署chatgpt-web-midjourney-proxy

有了上面部署chatgpt next web的经验,部署这个应该很简单。这个应用支持绘图功能,包括Dall.E, MJv6等,以及发送语音或者图片进行更加高级的交流。

docker pull ydlhero/chatgpt-web-midjourney-proxy #第一次安装不需要 更新需要
docker rm -f chatgpt-web-midjourney-proxy #第一次安装不需要 更新需要
docker run --name chatgpt-web-midjourney-proxy  -d -p 6015:3002 \
-e OPENAI_API_KEY=hk-你的apiKey \
-e OPENAI_API_BASE_URL=https://api.openai-hk.com  \
-e MJ_SERVER=https://api.openai-hk.com  \
-e AUTH_SECRET_KEY=访问授权密码-需要英文字母和数字  \
-e MJ_API_SECRET=hk-你的apiKey  ydlhero/chatgpt-web-midjourney-proxyCode language: PHP (php)

服务起来会绑定在上述 0.0.0.0:6015上,记得放开网页上的安全组设置。然后就可以访问公网IP:6015了。

写在最后

部署是很简单的事情。对于ChatGPT的应用才是真正要思考的地方。目前OpenAI已经有一个大而全的模型,而我们能做的,应该是在这个模型的基础上去搭建应用场景。现在最缺乏的,应该是AI 做事情的能力,而做的事情是五花八门的,很难统一处理,这也就给各个AI应用场景留下了开发的空间。可以想象成AI成为大脑,我们可以提供其各种各样的能力,如收集信息的能力(已有),网购下单的能力,记忆能力等等,那么当AI的能力越来越丰富,他的价值就会越来越大。

所以我觉得目前各大厂商往大模型上卷的意义可能还是在基础构建这一块,但真正造福你我,潜力无穷的应该是在应用场景的探索上。这一块我只看到了一些小厂家在做一些特定的场景,如文书写作等,但面太窄了。而面太窄的原因还是因为目前AI做事情的能力并不丰富,所以他能撬动的领域不多。

所以我猜,下一步AI应该是丰富其做事能力。这光OpenAI自己做是不够的。所以他是属于你我的机会。

星之海 Sea of Stars 通关有感

这是一个非常另类的JRPG。一方面,他具有传统JRPG的所有特点:像素风,回合制战斗模式,解谜。另一方面是在传统上做的一系列改良,尤其是对战斗系统的改造。

特殊的战斗系统

战斗系统应该是JRPG中最为核心的组成部分,因为他和角色的成长,剧情的推进,数值循环都息息相关。与传统JRPG游戏以回合制战斗,丰富且复杂的技能体系,针对人物特点的装备系统作为主要战斗体验不同的事,星之海采用的是类似于QTE的战斗体系。

其特点可概况为:

  1. 普通攻击获得MP。
  2. 技能攻击可以追加QTE支持。在攻击命中瞬间,或者特定时间点点击或者释放特定按钮可以让伤害值或者效果获得加成。部分群体攻击技能可以追加攻击次数。而在防守时,在特定时间点反击(类似于塞尔达的盾反),也可以减少受到的伤害。
  3. 怪物施法时头上会标记特定的属性,玩家可以构造出此属性,可以打断怪物的施法。
  4. 在盾反,技能释放后可以获得COMBO(技能点),可以释放角色间的联合技能(组合技)。但每次战斗后会清空。
  5. 战斗中会获得点数,以释放每个角色的终极技。

这套系统中的QTE元素为战斗中的纯拼数值体验注入了一点活力。前期的战斗体验不再时我打你一棍你咬我一下的新手村模式,而是可以给到丰富的,即时的战斗体验。也的确时这套战斗系统让我眼前一亮。他的QTE的结合不算突兀,也巧妙,战斗的乐趣感让我即时在明雷的情况下也不愿意放弃打小怪。

而本作的Boss战非常多,而且特点鲜明。有大大的Boss,也有群体作战的Boss。有一些需要达到特定的条件才可以攻击本体,否则会遭受大量伤害。有一些会带有秒杀技能,需要全力打断。融合了QTE的快感,在Boss战斗中的表现会更加鲜明。所以合适的技能选择,会给到玩家小刀屠巨龙的快感,加上各种终极技能,也能够有主角高光展示的时刻。

从战斗到无趣

但成也萧何,败也萧何。这套系统的战斗体验在前期的确给到了很大的不一样,但在玩家逐步熟悉战斗后,会开始趋于无聊。而引入这套系统,却给整体的外围系统的设计带来了很大的变化。

战斗的无聊在后期开始显现。总共7角色,每个角色技能只有4个,加上地图上零零总总的组合技下来,大概5-6个组合技,加上每个角色一个终极技,能用到的技能不过10个内,频繁使用的技能大概每个角色只有1-2个。(扎莱的火球我从新手期用到打boss……)所以技能的丰富度完全不足。通篇下来,普攻 + 火球 + 月亮回旋 + 组合技回血,是我用的最多的技能。其他大部分技能都是冷藏。

组合技这个设计也常见于其他游戏,但这里面不同的是,组合技只在一个战斗中累积,下次战斗会从新累积,这就导致你不会秒杀机会,只剩下不停的普攻 + 小火球…… 这样的体验到后期可谓是无聊透顶。但尽管是明雷,有些雷你又跳不过去,只能乖乖打。

由于技能不足,带来的致命缺点是,难以感受到主角的成长。以前的JRPG,你要是回到新手村,那伤害往往是爆炸性的,而我们星之海,你就只能继续手搓大火球,造成差不多的伤害。那打了这么久,我们到底成长了什么?当剧情里主角说:感觉充能能量的时候,开启Boss战后我也还是继续一刀50一刀刀砍,完全感觉不到变强。而角色升级带来的加点对角色成长也不会有太多的帮助,你不能自由选择你要发展的方向,因为你的选择不会太多,且通关在20级左右,其实这些选择加点对整体角色不会有太大的影响。

显然设计师是为了QTE系统做了不少让步。尤其是外围系统。星之海的外围系统,过于简单。每个角色只有武器,装备,两个饰品。角色就物理攻击,法术攻击,物理防御,法术防御,血量这几个属性。所以在路过商店的时候,你根本不用挑选什么,直接对比下数值就可以,反正大差不差,而且相当不重要。游戏的战斗强调的还是QTE战斗,你的装备属性不会有太大的效果。而且各种装备的属性也是一幕了然,完全不会有纠结的时候,就像主角,他的武器一般就只有5-6阶,各种不同名字的剑,加不一样的攻击力,一眼就能知道哪个好哪个坏。像一些成名的JRPG游戏,往往复杂多样的装备体系,以角色养成为主要的卖点,星之海的反其道而行,会付出相应的代价。

总而言之,QTE的加入为前期打满了好感度,却为角色的成长度,数值的深度造成了太大的干扰。

一些设计的权衡

剧情,关卡,子系统,在星之海中,也能看出有一些不一样的想法在其中。

剧情可谓是上天入地,上能到太空,下能到海底,从古朴小村庄,到赛博科技城。有魔法,有科技,有恶龙,有魔王,还能体验到平行世界。在剧情上的确堆叠了很多很多元素,这在其他遵循统一世界观的JRPG中是很难见到的。但至少在我看来,各种概念上的融合不算生硬,但也难免会有魔法,科技,炼金术是怎么融合的疑问。显然这些都无关紧要,跟着剧情走,设计师也能自圆其说。

全剧让我印象深刻的是:加尔的死亡。加尔是我最喜欢的角色,却偏偏成为了唯一死亡的主角团成员,实在让我不甘心。所以那个时候多少是带着点心流去完成加尔的最后愿望,并走上星之海之路的。而其他由于过多元素的关系,我已经能难理清楚他们之间的关系,所以剧情上感觉比较跳。

角色间台词也很简单。说他是力透纸背也好,说他是表达不足也行。相信设计者并不希望用复杂的言语来表明角色间的关系和情感,而是在用词上比较斟酌,有点惜字如金的感觉。但这点不罗嗦的风格还是比较讨喜我,只是在需要情感表达的时候,其实可以多费点笔墨,尤其是在关键抉择上,我不介意他们有更多的心理话。

关卡上,高质量的像素风画质,加上地图上一些比较巧妙的视角设计,的确能给到不少惊喜。在关卡上的解迷难度适中,在主角技能吹风 + 飞绳的基础上发展出比较综合的谜题设计。

但整体的节奏把握度还是不够。前期的关卡要全要素跑完的话,还是比较费时间的,那时就让我对于后期关卡有了更高的期待,结果中后期的关卡显得过于简单。或者可以说整个关卡的体验是比较平的。后期Boss前关卡反而简单了许多,让我非常疑惑。

子系统中主要是钓鱼,烹饪,小游戏。他们在游戏过程中扮演着无足轻重的角色。显然还是因为战斗系统的设计原因,导致子系统不能很好展开。这就出现了钓来的鱼加上捡来的蔬菜烹饪获得药品,但角色技能也涵盖了大部分药品需求,且我普攻还能补蓝,很多时候药品并没有太大的作用。而这就进一步削弱了这些子系统的作用。我坦言,在游戏中,我烹饪的次数可能只有20次左右,而且我也不会关注我调到了什么鱼,捡到的蔬菜可以做什么,这让这套系统的存在感非常低。可以对照一下《旷野之息》,就可以明白一个让人爱不释手的烹饪系统应该是怎样去架构,星之海显然把实际作用这个层次削弱了,那么只能剩下一些收藏作用了。对于一个讨厌收集元素的我来说,肯定是跳过跳过。

小游戏我倒是玩了不少。就是觉得平衡性不足,基础棋子就是很难打败电脑。所以就放弃了。但也不奢求什么。

总结:还不错,可以试一试

作为一个不一样的JRPG游戏,的确可以试一试,看看设计者在其中的权衡。但如果是本着传统JRPG,像我这种思忆《黄金太阳》系列的玩家,应该会比较难获得好的体验。但如果是苦于传统JRPG的一些设定,如频繁地雷,复杂系统等的玩家,本作应该还是能给一点惊喜的,尤其是其战斗系统,加上精美的像素画风和适中的解谜元素,让人梦回小时候的时光,因为那个时候,我们也都是刚刚开始玩游戏,一切都像新的一样。

奥密克戎初体验

2022年 12月18日 周日

周末休息在家,看到新闻上各地都已经逐步放开,正在面对新冠疫情的第一波感染高峰。没想到,正当我感叹病毒传染速度之快时,我的妻子开始感觉身体不适,开始发烧,我才意识到,或许,我家的第一波也到来了。

或许早有准备,其实也没有多做准备。家里的药物对于两个人来说,应该是足够的。20颗布洛芬退烧,少量的中成药用来缓解症状。准备出去买点,才知道连深圳都已经出现了药品供应不足的现象,搜索一圈无果。我向公司说明,开始了居家办公,一边照顾妻子。

妻子的症状并没有非常剧烈,但也经历了非常难受的几天。先是发烧持续在38~39度之间,喉咙痛,鼻塞。晚上因为鼻子的原因,辗转难眠。整个人非常虚弱。发烧时用布洛芬 + 物理降温控制,倒没有出现持续性的高烧,算是幸运。家里没有专门的医用冰袋,用的是平时买生鲜剩下的冰袋,冻得结实。用卫生巾包裹起来,放在额头上。就此一招,体温得到了很好的控制。

我照顾妻子三天,她的症状逐步减轻,发烧也停了,剩下咳嗽。我这三天全程无防备照顾,确依然没有变阳,内心有小窃喜。或者我就是传说中的无症状感染者?又或者自己的体质不错?

2022年 12月21日 周三

就在我老婆逐步康复时,我中招了。之前的种种推测,纷纷打脸。发烧启动。我的症状有点意思,低烧37.8度,烧一会,停一会。停了的时候我变得精神,还能做点工作。晚上似乎会更糟糕,往往会发一会烧,38度不到,持续个把小时。这种情况持续了4天之久,直到周六我才能宣布自己已经从发烧中缓过来了。
但是喉咙刺痛,鼻塞也一直困扰着我。直到今天,12月28日,这两种症状才离我而去。最难受的其实是鼻塞,因为这个睡觉特别难。有一晚因为鼻塞的困扰,我到3点才昏昏睡去。这也对我的恢复有影响。所以现在看来,我最后悔的便是,家里没有一个缓解鼻塞的药。
从周六起,我的症状就好了很多了。周日我和妻子甚至还跑了趟广州,身体也已经没有大碍。只是咳嗽还有,在逐步减轻。

12月28日中午,自测抗原,阴性。意味着我的这次奥密克戎初体验就完成。我是幸运,没有经历持续高烧,症状相对起来比较轻微,我们用了大概10天的时间就度过了这次疫情的感染,或许奥密克戎真的要温柔很多。而我的部分同事就没有那么幸运,他们的症状明显得多,持续高烧不退的也有不少,可谓非常难受。希望他们能够快速恢复起来,一起用健康的身体迎接2023年的到来。

放开 仓促吗?

11月底的政策快速转向,也让我这个坚定的共存派惊呆。三年的努力在几天间就完全转弯。高情商是说:从侧面论证了上面的果敢和民心所向。低情商便是仓促,医疗挤兑,超额死亡,种种意外。坦白说,的确没有充足的准备(包括充足的时间,医疗物资,人们的培训,宽慰),社会乱象横生也是有的,黄牛与自发救援,无法就医,哄抬药价,殡仪馆爆满等。深圳作为一线城市,拥有最大的年轻人群体,在放开防疫后,城市基本进入了停摆状态:路上行人寥寥无几,公司接连放假,堂食停止…… 大部分人都在家里肉身抵抗病毒。按照大概10天痊愈来计算,深圳从12月20号左右开始爆发,要到元旦后才会逐步恢复人气。

这么看来,的确是放开得有点仓促。春节假期临近,各地都要过个好年,这在中国是一个普遍的朴素愿望。封控再拖到节后恐怕对于士气的打击巨大,对经济的打击巨大,防疫的支出也会达到无法估算的程度。传言多地因为封控已经到了山穷水尽的地步。不止是城市的管理者,更有生活在其中的人民,多有到了吃谷种的地步了。情况越拖越糟糕,二十条的放开后,各地其实基本都已经不按照二十条执行了。因为二十条的意思也很明显:要放开。既然晚放后果这么严重,那么最优的选择就是现在马上放开,停止封控支出,加强医疗资源。相信各地管理者也已经竭尽全力,调控资源,尽力减少伤亡。所以说仓促,是仓促,也不仓促。这就是最好的时间点。

用10天换零星不绝的封控,于我而言,太划算了。用一次艰难的生病体验,换得未来年内的自由自在,也是比好买卖。诚然放开会付出代价,但只要这个代价短期内可以接受,那么未来可期。

Switch 软破流程记录

我从我弟那里顺来的switch已经吃灰很久了,偶尔看到了switch破解的文章,发现有破解的可能,故打算试一试。此文仅供记录学习。

破解原理

switch的破解分成两种:硬破和软破。他们都是利用了芯片的漏洞(进入Nvidia T210的工程模式,RCM),运行自制代码。这就意味着可以引导到自制系统中,从而跳过NS自带系统的限制。
两者的区别在于硬破需要拆机焊接,需要有焊接经验,操作麻烦,好处是一次操作,终身受益。同时没有版本的限制,只要switch都支持硬破。而软破,就不需要焊接,通过一个特殊的装置(可以是RCMLoader,安卓手机,电脑),可以进入到RCM中。但在新版本的switch中,官方屏蔽了这种做法,所以只能在老机器中可行,目前说是2018年6月前的机器。且每次关机,都需要重新注入,引导到自制系统中,稍微麻烦。

其实两者的目标都是一致,即进入到RCM中,引导进入自制系统。所以在破解后使用上是完全一致的。

目前在网上,不算switch机器的价格,硬破价格约400RMB,软破可以做到0成本,或者买一个RCMLoader,成本在50~80RMB,平时使用上会方便很多。我的机器恰好是老机器了,如何软破的要求,当然还是选择了软破。

大气层系统 软破过程

前期

  1. 购买RCMLoader(又称注入器),v5版本 52RMB。(v10版本修改payload的方式可以有更好的后续兼容性,+30RMB即可)
  2. 一条 Type-C USB线,用来switch连接电脑。最好先试一试,我这的一条华为的线,连手机没问题,连switch死活不识别。
  3. switch 内存卡。系统大概20G,由于要装虚拟系统(就是现有系统的备份),需要20G,总计40G,加上游戏,至少需要128G的内存卡。多多益善。
  4. usb读卡器。
  5. switch机器,可以将系统更新最新,以支持最新的游戏。买RCMLoader之前记得在卖家那里问问机器是否支持。最重要的是:将switch中的wifi记录全部删除,并开启飞行模式!

switch机器充满电。


开搞

下载整合包

确认机器系统为 15.0.1,在网络上查找15.0.1的整合包。通常搜索下很容易获得。切记,机器的系统版本要和整合包的版本一致。(我在这里卡了很久,用了14.0.1的整合包,一直fatal error)

复制整合包内容到switch卡中

解压后,直接将整合包的所有的内容复制到TF卡的根目录中。这些内容其实是引导程序,只要被运行,就能引导switch启动别的系统。

启动RCM模式

将tf卡装回去switch中,switch关闭电源(长按电源键,选择关闭电源),拆下switch两个手柄。
RCMLoader有两部分,一个注入器,本体,带有Type-C接口,以及一个短接器。

RCMLoader

第一步:将短接器On端朝下,由上往下插入switch右侧手柄的位置中。
第二步:将注入器插入switch Type-C接口中。

现在准备开机。右手按住音量键+号,左右按1下电源键,直到画面出现大气层系统。正常情况下,就会进入到这个界面:

接下来可以拔下短接器,注入器,插入手柄操作了。

制作虚拟系统

点击系统右侧的emuMMC图标,然后点击Create emuMMC,在弹出框中选择SD File,等左侧进度条走完,右侧会显示三个Done。

此时返回一路Close,返回到最初的界面,选择Launch或者启动,就可以看到制作好的虚拟系统以及原本的正版系统。让我们进入虚拟系统即可。

此时破解已经完成。

软破的机器如果关闭电源重启,需要再走一遍RCMLoader流程,在Launch界面选择进入虚拟系统。流程上会稍微繁琐些。对于使用安卓或者PC的发射方式,会更加麻烦,所以我推荐还是直接买个RCMLoader方便得多,还能随身携带。

安装游戏 DBI

下载游戏

网上找switch游戏资源,现在很多网站都有。如果没有百度网盘会员的话,首选BT方式下载,热门游戏下载速度非常良好。NS 游戏下载
一般是NPS和XCI格式,均可以支持。

连接电脑安装

一般switch连接电脑会自动安装驱动,如果连接电脑没有反应的话,可以手动安装个驱动。里面有个Intall Driver的可执行程序。

将switch连接上电脑后,switch选择DBI(在相册中找一下),打开后是非常复古的界面,不用管,选择
Run MTP responder,正常情况下,电脑会有反应。如果出现电脑没有反应的话,大概率是线的问题。

此时电脑会有Switch盘可选,点进去后:

选择 MicroSD install,里面只有一个文件,名字是说将NPS或者XCI的文件拖进去即可。
那么就将下载好的游戏拖进去吧!

等待进度条走完,switch那边也会显示complete,退出DBI,你的游戏已经安装好,可以用了。

为什么使用DBI?

方便。tf卡fat32格式,不支持大于4g的文件,拖进去再到switch里面安装非常麻烦。

  1. 先要将游戏文件拆分。有工具可以做到。
  2. switch拆下tf卡装到电脑上,复制进去。
  3. 由于tf被拆下,switch只能重启,需要重新引导。
  4. 在switch中执行安装程序。
  5. 删除tf卡中的游戏文件。
    流程长,不方便,tf的剩余容量必须是安装游戏的两倍以上。而DBI方式就简单很多。没有了重新引导,你的机器可以长时间不重启,没有烦恼。

Python获取基金信息

背景

目前市面上有大量的基金交易平台,除了传统的银行渠道,还有很多互联网金融平台。在最早期的时候,各家的申购手续费并不一致,可以购买的产品也不一样。这就导致了几年下来,我的基金遍布了三大平台,每次检查收益情况和资金分布,都要跨多个平台,操作起来非常麻烦。久而久之,对自己的投资情况都已经混淆不清了。

于是,打算做一个能够整合自己的投资情况的工具,基金数据的整理就先试一下水。采用了akshare作为基金当前数据来源,工具整合整理并制表输送到邮箱中。(当然,最靠谱的方式无异于自动登录各大平台,并获取账户的投资信息汇总,只是各大平台并不提供接口,且数据形式各异,整理起来也非常困难,所以选择了外部数据源的方式。)

最终效果

实时获取当前的基金价格,并计算出投资的收益,生成图表并发送到邮箱中。

实现

python小脚本实现,基于 akshare + dataframe_image 库完成。也调研了下tushare,但是这个积分机制让我望而却步,就先不用了。理论上,akshare完成的工具其实更像是数据清理,他本身并不做数据的存储,所以每次发起请求,都是直接到目标地址上拉取数据,并本地整理返回。这就注定了akshare的处理耗时很高,单个接口的处理耗时可能到10s+,用来做量化,恐怕有点难了。而tushare,理解上是会自动采集数据,并缓存在服务上,供调用方使用。接口的处理耗时比akshare要好很多。但也因此,需要服务器成本,带宽成本,价格不菲,也就需要这个积分机制来维持。

目前我只是小试牛刀,且功能上对时效性要求并不高,可以接受分钟级别的延迟,akshare自然更适合我一些。

由于在我的电脑上,并没有利索的python环境,所以临时配置了下python环境,使用pyCharm作为IDE。(事实证明,这并不是一个好主意)

akshare 安装

使用了fund_open_fund_daily_em接口,直接把全部的基金信息拉回来处理。这里面的数据结构我并不熟悉,也浪费了大量的时间在查接口上。不过幸好用的pandas的数据结构,学起来也不算复杂,也算为后续的累积点经验了。


# get_hold_status 通过获取购买的基金的最新数据,与本金数据对比,生成DataFrame表格
def get_hold_status(my_holds):
    all_fund_daily = ak.fund_open_fund_daily_em()
    indexed = all_fund_daily.set_index("基金代码", drop=False)
    status = []
    all_total_price = 0
    today_all_earn = 0
    yesterday_all_earn = 0
    for code in my_holds.get_holds_code():
        try:
            fund_info = indexed.loc[code]
        except BaseException as e:
            util.log("get %s fund info failed" % code)
            util.log(e)
            continue

        hold_info = my_holds.get_hold_info(code)
        total_invest = my_holds.get_total_investment()
        row = {
            "基金代码": fund_info.loc["基金代码"],
            "基金简称": fund_info.loc["基金简称"],
            "更新时间": get_date_from_column_name(indexed.columns[2]),
        }
        yesterday_price = float(fund_info.iloc[4])
        if fund_info.iloc[2] != "":
            today_price = float(fund_info.iloc[2])
        else:
            today_price = yesterday_price
            row["更新时间"] = get_date_from_column_name(indexed.columns[4])
        row["是否更新"] = fund_info.iloc[2] != ""
        row["当前价格"] = today_price
        row["今日收益"] = (today_price-yesterday_price) * hold_info["share"]
        today_all_earn += row["今日收益"]
        yesterday_all_earn += yesterday_price * hold_info["share"]
        row["总收益"] = today_price * hold_info["share"] - hold_info["total_money"]
        row["今日涨幅"] = (today_price - yesterday_price) / yesterday_price
        row["总涨幅"] = row["总收益"] / hold_info["total_money"]
        row["本金"] = hold_info["total_money"]
        row["本金占比"] = hold_info["total_money"] / total_invest
        all_total_price += today_price * hold_info["share"]
        status.append(row)

    status.append({
        "基金代码":"总计",
        "基金简称":"---",
        "更新时间":"---",
        "当前价格":0.0,
        "是否更新": True,
        "总收益":all_total_price - total_invest,
        "本金": total_invest,
        "总涨幅": (all_total_price - total_invest) / total_invest,
        "今日涨幅": today_all_earn /yesterday_all_earn ,
        "今日收益":today_all_earn,
        "本金占比":1.0,
    })

    # 这里并不采用DataFrame的汇总,而是手算了一个,主要是我的汇总信息相对复杂
    return pd.DataFrame(status[:len(status) -1]), pd.DataFrame(status[len(status) - 1:])
Code language: Python (python)

接下来,是将DataFrame的数据美化成图表输出。这里研究了很久,这篇文章给了我极大的帮助:

Pandas表格样式设置指南

    pd.set_option('display.max_rows', None)
    pd.set_option('display.width', 5000)
    pd.set_option('display.unicode.ambiguous_as_wide', True)
    pd.set_option('display.unicode.east_asian_width', True)

    pd.options.display.max_columns = None
    my_hodls = myfunds_config.MyFundsInfo()
    status, total = get_hold_status(my_hodls)
    status.sort_values(by='今日收益', inplace=True, ascending=False)

    status = status.append(total, ignore_index=True)

    styler = status.style.hide(axis='index')\
        .format({'今日收益':'{0:.2f}', '今日涨幅':'{0:.2%}', '总收益':'{0:.2f}', '总涨幅':'{0:.2%}', '当前价格':'{0:.2f}', '本金':'{0:.1f}', '本金占比':'{0:.2%}'})\
        .bar(subset=['今日涨幅', '总涨幅', '今日收益'], color=['#99ff66','#ee7621'], align='mid') \
        .bar(subset=['总收益'], color=['#339933','#FF0000'], align='mid')
Code language: Python (python)

输出成png图片保存

import dataframe_image as dfi
# 注意这里是styler
dfi.export(styler, "test.png")Code language: PHP (php)

发送邮件(带附件)

import os.path
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import util


class EmailSender:

    def __init__(self, account : str, pwd : str):
        self.account = account
        self.pwd = pwd
        self.inited = False
        self.att_list = []
        try:
            self.smtp = smtplib.SMTP_SSL("smtp." + account.split("@")[1], 465)
            self.smtp.login(self.account, self.pwd)
            self.inited = True
        except BaseException as e:
            util.log(e)

    def attach_file(self, file_name: str):
        att = MIMEText(open(file_name, 'rb').read(), 'base64', 'utf-8')
        att['Content-Type'] = 'application/octet-stream'
        att['Content-Disposition'] = 'attachment;filename=%s'%(os.path.basename(file_name))
        self.att_list.append(att)

    def send_mail(self, to: str, subject: str, content: str):
        if not self.inited:
            util.log("smtp not inited!")
            return False
        msg = MIMEMultipart()
        msg.attach(MIMEText(content, 'plain', 'utf-8'))
        for att in self.att_list:
            msg.attach(att)
        self.att_list = []
        msg['Subject'] = subject
        msg['From'] = self.account
        try:
            self.smtp.sendmail(self.account, to, msg.as_string())
            return True
        except BaseException as e:
            util.log("send mail failed")
            util.log(e)
            return False

Code language: Python (python)

最终,将流程串起来,每5分钟拉取一次信息对比是否发生变化,如有变化,发送邮件。将其部署到服务器上,持久运行。

问题

Windows上开发,Linux上部署

由于这两个平台的差异比较大,python的某些库兼容性并不是很好。加上两边我都需要初始化一遍环境,所以在调试环境的时候遇到了很多问题:

  1. linux服务器选啥:腾讯云轻应用-广州机房
  2. 代码托管:开始并没有考虑这个问题,后来感觉很有必要。采用腾讯云coding平台。
  3. 远程开发? 由于本地的终端并没有好用的上传方式,最终还是选用了熟悉的vscode远程开发模式。
  4. dataframe_image输出中文乱码:这是因为linux服务器上没有安装中文字体库。
  5. 需要chrome:dataframe_image默认采用的是chrome的screenshot方式渲染表格生成图片。要求机器上需要安装chrome。在我的Windows机器上没啥问题,Linux上安装chrome就需要手动安装了。
wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
sudo yum install ./google-chrome-stable_current_*.rpmCode language: Bash (bash)

一些思考

以前自己写一个小工具,想起什么就开始搞。这次走了很多弯路。

  1. 代码托管,项目计划,wiki,CI/CD 是需要考虑好的。在小工具上可以尝试使用Serverless的方式来做,容器化部署的模式也可以做进一步探索,尽量使用市面上成熟的产品,不要过多依赖闭源能力,这样也是对自己能力的锻炼。
  2. vscode 远程开发模式必须坚持。申请机器,还是第一步把开发环境定好。
  3. python的代码规范,提示兼容,文档化。以前我写c/c++从不考虑这些问题,现在工作上用了很多golang,也觉得对于维护代码质量,多人协同开发下,规范是必然的。目前我对于Python这块是空白,还有很多写法也不能确认是否最佳实践,有必要研究下。

后续我会继续维护这个个人项目,将股票的信息整合进来,尝试做简单的分析。

跳出生活的舒适圈

近几年来有很多的书籍,文章,都在劝告众人:要跳出舒适圈,方能实现自我突破。我知道这个观点已经许久,并深以为然。但在我一贯的见解中,走出舒适圈,总是对应诸如:命运的转折点,一个改变人生的决定,一次勇敢的冒险,一个惊天的自我发现。这与我的日常生活,并无太多的关联。只有在我遭遇到挫折,变故时,这个观点能多少给我点安慰。

近几天的休假中,我又再次进入了舒适模式:玩喜欢的游戏,做葛优的姿势,吃个不停的零食,睡不满的懒觉。与我之前的工作状态大相径庭。奇怪的是,当我每日起来,熟练完成我的舒适动作,我可以一整天都保持这样的状态,看日出日落,时间过得很快,美好假期就这样过去了。似乎与我以往的每次假期一摸一样。其实这又未尝是一种舒适圈?

这是一种生活的舒适圈。经年累月的习惯,能让人选择以最舒服的姿势度过每一天。

意识到这一点,就能将跳出舒适圈的概念融入到生活之中。跳出生活的舒适圈,不是在生活中磨练自己,而是跳出生活中的惯式,去过别样的一天。可以是换个地点,换个食物,换个运动,换个时间,体会生活中的不一样。也可以是在以往的习惯中添加花样,换本书,换个up主,说不定也有新的发现。

似乎这种观点,与原有的跳出舒适圈的观点是不一致的。更确切的说,跳出生活的舒适圈,是给生活增加变数,打破原有的生活节奏。而不是让去挑战什么,击败什么。但在某些点上,他们又是一致的。要改变长期以来的生活习惯,也是需要勇气,毅力的,尽管不如学习新的技能,从事新的行业一般需要惊心动魄的勇气,但谁又能说改变生活的“火车头”行进路线是一件容易的事情?

起码于我而言,就不是一件容易的事情。

身边的人,物,外地的朋友,远在天边的新闻,无一不在影响着生活本身。而我们自发形成的习惯,可能已经是在各种影响下的均衡产物。要打破这种循环,并将生活往更好的方向指引,并非易事。更何况很多人,并不能支配自己的生活,得过且过。像我,以为自己能够控制好自己,其实也是在这个舒适圈中徘徊挣扎许久。

2022-05-03 一个阅读空间

假期以来,我起码有3.5天的时间都是像之前那样活着。而现在,我决定要跳出来,我已经清楚,这样的生活节奏,并不能为我的目标服务,除了在人身挑战中要跳出舒适圈,在生活中,我也要跳出来。于我,最简单的改变方式是,换个地点。将想要完成的任务与地点关联,想要读书,找个幽静的地方,必须运动,换个运动场。不要再试图将家里改造成不同的模样,那只会浪费精力。用少量的时间,获得不受干扰,不被动摇,又能跳出生活懒惰循环的良好环境,何乐而不为呢?

想到如此,下定决心要对自己以往的生活习惯进行改造,原则上是要跳出“坏的习惯与循环”。方法上要尽快行动,特定空间,不断变化。

尽快行动

为了避免拖延,懒惰。要完成一件事情,抛开计划和目标而言,最重要的是要尽快启动起来。尽管身体已经很排斥,尽管也知道后续的难度会非常大,但尽快行动,总比浑浑噩噩拖到最后勉强交差强。尽快行动起来,一方面能够节省下担忧害怕的时间,也能够将实际遇到的问题抛出,以便寻找解决方案。

而在每天的生活中,尽快的行动意味着,早起,早行动,尽早开始当天的事宜。不要总想着来一把游戏就工作的想法,因为一旦进入到每日的舒适圈中,意味着今天大概率会步后尘。

特定空间

因为我的书桌就在卧室中,每次偷懒总要往床上躺一下。曾经也想过,要将房间改造成一个更加合适的工作空间,只是现在看来,当前的房子没有多少改造的余地了。那么我面对的就是各种各样能够直接触达到我的诱惑。

所以我只能选择去到一个特定的空间,来完成自己的工作。之前我并不懂得,为什么有些人宁愿去酒店的咖啡厅工作阅读呆坐,有些人愿意去吵闹的星巴克捧着电脑敲代码,现在算是有点头绪。不是每个人的居住空间都能够满足每天生活所需。为了完成特定的任务,去特定的空间,是一个十分方便的解决方案,尽管可能会费一点钱,和时间,但总比在充满诱惑的家中来的便宜得多。

不断变化

在没有尝试到一个可行的,完美的每日解决方案之前,不断变化和寻找更优解。其实,在生活中哪有什么完美的解决方案,经历得多了,其实每次的选择也只能说是当下的最好解决而已,但不代表每次均能获得如此的结果。所以方法就是,多去尝试不同的解法。这也是跳出生活舒适圈的重要思想之一。

生活并非一成不变的,不要指望在变化中找囊括所有的解决方案,希望每次都能找一个合适的方案,那便足够了。


今天幸好还是从家里跑了出来,尽管在出来之前我非常不情愿,结果证明多少还是有点改进的。但愿能一直保持这个想法并一直实践下去。