Python 里的 random 模块

最近工作上遇到了一个比较有意思的问题:在生成邮箱验证码的时候,居然会出现重复,而且是可复现的重复,后来去另一个环境试了下,发现居然不同环境都会重复!

这种情况,粗略判断,应该是随机数生成的问题,但是奇怪的是,代码也重置了 seed,这个 seed 也是个随机的:

1
2
3
4
5
6
7
8
import random

random.seed(time.time() * 1000)


def get_capture(length=6):
all_letters = string.ascii_uppercase + string.digits
return ''.join(random.sample(all_letters, length))

经过调试,发现不论 seed 设置什么值,生成的随机字符串都是一样的,但是如果把 seed 设置放在函数内,就又都是随机的了。

最开始以为是 seed 设置会有作用域区分,但是经过查看代码,发现 random 居然是在内部生成了一个 Random 类的实例,从外部导入的 random.seedrandom.sample 都是在这个实例上调用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Create one instance, seeded from current time, and export its methods
# as module-level functions. The functions share state across all uses
#(both in the user's code and in the Python libraries), but that's fine
# for most programs and is easier for the casual user than making them
# instantiate their own Random() instance.

_inst = Random()
seed = _inst.seed
random = _inst.random
uniform = _inst.uniform
triangular = _inst.triangular
randint = _inst.randint
choice = _inst.choice
randrange = _inst.randrange
sample = _inst.sample
shuffle = _inst.shuffle
choices = _inst.choices
normalvariate = _inst.normalvariate
lognormvariate = _inst.lognormvariate
expovariate = _inst.expovariate
vonmisesvariate = _inst.vonmisesvariate
gammavariate = _inst.gammavariate
gauss = _inst.gauss
betavariate = _inst.betavariate
paretovariate = _inst.paretovariate
weibullvariate = _inst.weibullvariate
getstate = _inst.getstate
setstate = _inst.setstate
getrandbits = _inst.getrandbits

那既然不论在全局 seed,还是在函数内 seed,都基于同一个实例,那应该都起作用才对。现在的状态是全局调用 seed 函数,不论传入什么值产生的结果都是一样的。而在函数内调用 seed,如果输入是随机值,那么输出也是随机的,如果输入是个固定的,那么输出也会是固定的随机字符串。

那么这样看来,似乎只有一种可能————有其他地方在这个全局实例上也调用了 seed,于是我把断点打到这个 seed 函数里,然后重启 web,果然发现有很多次调用。

顺藤摸瓜,找到了其他在全局调用 seed 的地方,案子终于破了:其他地方调用 seed 的时机比这个地方迟,而且又都是在一个实例上调用的,那肯定都覆盖了,而且其他地方都是以固定数字调用 seed 的,所以最后产生的随机字符串都是以固定的顺序生成的。

那么如何修改代码呢?

在调试的时候,除了上面的那些全局调用 seed 的地方,还有一些其他写法,那就是新实例一个 Random 实例,让它和 random 模块里内置的那个默认实例区分开,然后后面调用 random 方法的地方,都基于这个新实例。妙啊!

那么 random 库使用的最佳实践应该是 在用到 random 的时候,搞一个自己的 Random 实例,这样就分开了。

代码修改如下:

1
2
3
4
5
6
7
8
import random

_random = random.Random(time.time() * 1000)


def get_capture(length=6):
all_letters = string.ascii_uppercase + string.digits
return ''.join(_random.sample(all_letters, length))

Get新技能——接网线

学会新技能总是令人振奋,最近学会了如何给网线压水晶头以及如何将网线接到网线插座上。

基本知识

首先一根网线里面有8根线,分别有不同颜色的外皮,如下图

image.png

一般来说有两种接线方法,即上图中的T568A和T568B。以前还需要区分这两种线序,其中网线两头都是T568B被称为直通线,用于不同设备的连接,比如电脑和路由器;网线两头一边是T568A接法,一边是T568B接法,这种被称为交叉线接法,常用于相同设备的连接,比如电脑连接电脑。

是不是觉得头大,哪个该接哪个呢,所幸的是现在的设备都已经具备了自动逆转的功能,所以我们只需要全部接一种接法就好了——通通按T568B接法进行连接。

我家的网络环境

我家的户型图以及网络规划如下图:

image.png

红圈的地方是弱电箱,光猫在这里,然后蓝圈的地方是主路由器,绿圈的地方是网络插座。然后光猫往主路由器有一根网线,主路由器往两个网络插座各有一根网线。

在过道最里面这个网络插座附近将来如果需要会配置一个路由器,和主路由器形成一个mesh,以弥补主卧和卫生间区域网络信号的不足。

现在我需要做的工作就是:

  1. 网线和两个网络插座相连
  2. 弱电箱处有一根网线需要压水晶头
  3. 客厅主路由器处有三根网线需要压水晶头

接网线插座

之所以先介绍如何接网线插座,因为网线插座接起来最简单,哈哈。

无论是网络插座已经装到墙上了还是没有,反正想办法把接线端子搞出来,接线端子上有A,B两种标识,然后对应有接线的颜色,我们就按B来接,如图:

image.png

接线就是将对应颜色的线压到每个对应端子里,然后用压线刀把线卡到里面,很简单,记得一定要卡严实了,当然也不要太大劲把端子整坏了。

线压好之后,先不要着急往墙上装,因为还有一步:检测线是否接好。

但是要进行检测,那么客厅区域的网线就需要将水晶头压好。

接水晶头

重头戏来了,水晶头一开始因为知识掌握不牢固,操作不熟练,直接废了3根水晶头,中途陆续又接废了几个,到最后4根网线竟然用了10个水晶头才接好🤣

好了,废话不多说,开始正题

确认网线规格

接水晶头第一步是要确认网线规格,可以粗略分为两类,六类之前和六类之后,为啥分为这两类,因为这两类使用的工具不一样,所以一定要先分清楚网线规格。其中六类之后目前就只有超六类和七类。

网线规格在网线外的包皮上可以找到,我家的是CAT6A,也就是超六类网线,超六类网线和七类网线工具是共用的,那么我就在京东上买了七类网线的压制工具。

压水晶头

上二维码,这是一个视频教程:

image.png

我就总结下要点:

  • 一定要确认好顺序

先按照橙白,橙色,绿白,蓝色,蓝白,绿色,棕白,棕色整理好线束,然后开始分层处理:橙白,绿白,蓝白,棕白放到下层,从下层穿过束线卡,然后橙色,蓝色,绿色,棕色,从上层穿过束线卡,留好足够的距离,将束线卡另一侧多余的线束剪掉。

  • 注意怼到水晶头里的方向

这里非常重要,我就是没注意这块,弄反了,白给了好几个水晶头。

方向是:花色线束所在的位置靠近水晶头有塑料弹簧片的一侧,纯色线束所在位置靠近水晶头有金属针脚的一侧

  • 怼进去

这里还需要注意,因为超六类和七类网线都比较粗,所以需要先把后面网线压扁,这样好怼进去。

怼进去,怼到头,这时候从水晶头往里面看,可以清晰看到各线束的金属触点,这样才算OK。

  • 压燕尾夹

用网线钳可以压燕尾夹,但是注意力道,不要把网线压坏了。我就是虽然水晶头接好了,但是用力过猛把网线压坏了,又一次白给。。

好了,到这里水晶头已经接好了,下来就是测试部分。

网络联通测试

工具箱里有网络测试仪,因为我需要测试的网线两端距离比较远,所以需要将网络测试仪分开。

然后分别插上网线,打开网络测试仪开关即可。

这里不论你看哪边的测试仪都可以,因为联通是亮灯,不通就不亮,所以只需要看一边即可。

image.png

总结

好了,到这里就全部结束了。

为啥我要自己接,因为我们工长说电工不会接,请外面人接需要300块,那我一想还不如我在京东上买套工具200块,自己接,还能学会一个新技能,何乐而不为?哈哈。

学习 React

以前也写过一些前端,比如我自己的博客,就是用 JQuery+HTML+CSS 实现的,但是大部分都是抄的,特别是 CSS 样式,真让人头大。至于 JS 部分,也是现学现撸,很不成体系,更不用说更深入的语言细节了,那就是一塌糊涂。

最近浏览博客,发现之前关注的博主 miguelgrinberg.com 开始在写 React 的教程,所以打算跟进学习一下,正好了解一下现代的 JS 框架是怎么样的,学习下前端工程是怎么构建的。

听说成为全栈工程师能更容易拿到远程工作的 offer,嗯不错不错,开始搞吧,学习使我快乐~

教程地址:Introducing the React Mega-Tutorial

我会在博客里记录一下有意思和对我来说重要的点,主要就是个笔记吧。

ES5 vs. ES6

ECMA, ECMAScript

transpiling:which converts modern JavaScript source code into functionally equivalent ES5 code that runs everywhere.

Summary of Recent JavaScript Features

Trailing Commas

很有帮助的观点:

1
2
3
4
5
const myArray = [
1,
3,
5,
];

在最后面一个元素加上逗号有两点好处,一个是上下挪动比较方便,另一个是新加元素比较方便。

Imports and Exports

这里其实没有完全理解,default export到底和其他export有什么意思,文中说了一句话:When using default exports, the name of the exported symbol does not really matter.

后面举的例子,说 import myReallyCoolFunction from './cool'; 也是有效的,那和 import myCoolFunction from './cool'; 是一个意思?

也就是说 default export 的东西,因为一个模块只能有一个,所以在其他模块引用的时候可以随便用任何名字?

先保留这个疑惑,继续往下看。

另外导入 non-default export 也有一点不一样,需要加大括号 import { SQRT2 } from './cool';

Variables and Constants

使用 let 来声明变量,const 来声明常量。

1
2
let a = 1;
const c = 3;

常量就是赋值之后不能有新的赋值了,而且也必须是在声明的时候赋值。

Equality and Inequality Comparisons

===!==

String Interpolation
1
2
const name = 'susan';
let greeting = `Hello, ${name}!`; // "Hello, susan!"
For-Of Loops
1
2
3
4
const allTheNames = ['susan', 'john', 'alice'];
for (name of allTheNames) {
console.log(name);
}
Arrow Functions

之前 code review 的时候看过这种写法,觉得很神秘,现在详细了解之后,其实也没啥,正如 Python 里的匿名函数,lambda 表达式一样。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function mult(x, y) {
const result = x * y;
return result;
}

// 函数用箭头函数可以这样写
const mult = (x, y) => {
const result = x * y;
return result;
};

// 可以这样
const mult = (x, y) => x * y;

// 如果没有 y 参数 可以这样
const square = x => x * x;

// 回调函数
longTask(result => console.log(result));

在 Python 里就会这么写

1
lambda x, y: x * y
Promises
1
2
3
4
fetch('http://example.com/data.json')
.then(r => r.json())
.then(data => console.log(data))
.catch(error => console.log(`Error: ${error}`));
Async and Await

上面的写法可以改成这样,更易读

1
2
3
4
5
async function f() {
const r = await fetch('https://example.com/data.json');
const data = await r.json();
console.log(data);
}

加上 async 的函数会自动返回一个 promise

箭头函数也可以使用 async

1
2
3
4
const g = async () => {
await f();
console.log('done!');
};
Spread Operator

Using column alias in a WHERE clause doesn't work

工作中需要写一个 SQL 进行查询,本来我是这样写的:

1
2
3
4
5
SELECT document.id, (COALESCE(CAST(document.internal_info->>'proofread_time' AS int), 0) - p500_end.end_utc) / 60 AS proofread_time
FROM document
JOIN p500_end ON p500_end.doc_id = document.id
WHERE document.created_utc > 1658592000 AND document.deleted = 0 AND proofread_time > 60
ORDER BY id DESC

但是发现报这样的错误:

ERROR: column "proofread_time" does not exist

然后我把 WHERE 条件里的 proofread_time 替换成 (COALESCE(CAST(document.internal_info->>'proofread_time' AS int), 0) - p500_end.end_utc) / 60,就正常了。

咦,好奇怪,难道 alias 不能在 WHERE 条件里用吗,有点反直觉,于是我去查了下文档:

An output column’s name can be used to refer to the column’s value in ORDER BY and GROUP BY clauses, but not in the WHERE or HAVING clauses; there you must write out the expression instead.

原因就是 WHERE 语句和 HAVING 语句是在 column aliases 之前做的,所以没法引用,而 ORDER BY 和 GROUP BY 是在其之后,所以可以使用 aliased column

很古怪吧,反直觉!

但是把这么一长串的表达式写两遍真的很难受,所以我使用了 WITH 表达式来解决这个问题:

1
2
3
4
5
6
7
8
WITH results AS (
SELECT document.id, (COALESCE(CAST(document.internal_info->>'proofread_time' as int), 0) - p500_end.end_utc) / 60 AS proofread_time
FROM document
JOIN p500_end ON p500_end.doc_id = document.id
WHERE document.created_utc > 1658592000 AND document.deleted = 0
ORDER BY id DESC
)
SELECT id, proofread_time FROM results WHERE proofread_time > 60;

这样看起来就清晰多了。

明天大阳线

有两个标的已经拿了9个交易日了,但是一直亏,真的今天我都想把他俩卖了,好来个解脱,但是交易系统说不卖,好吧,我忍住了。

我还是选择相信交易系统!

现在情绪已经到最极端的时候了,所以我觉得明天大概率大阳线反转!

不论这次结果如何,我都认了。

就这样。

——————————————

2022.10.26更新

已经全部割肉卖出,失败了。辣鸡!

封控在家的生活

自从上周五被封控以来,宅在家的生活就是:做饭,吃饭,上班,做饭,吃饭,睡觉。

image.png

什么时候疫情才能消失啊,炒鸡想出去玩,自驾新疆,西藏。。。

交易系统全标的收益率回测新高了

交易系统全标的收益率回测新高了,目前总收益率27.47%,平均每个月2.6%!

image.png

但是因为我是从2月份开始跟踪的,而且第三季度去玩了会股票,试了试其他策略,导致第三季度收益率-10%,而交易系统第三季度收益率是2%,这一下就差了12%。

哎,不能瞎搞了,管住手,按既定规则交易。

有人说,即使给你一个90%可能能盈利的交易系统,你也不一定能盈利,我深刻的体会到这句话是啥意思了。有人会觉得很简单,但是你实盘操作一下就知道有多困难了:

  • 你自己执行力是否够?
  • 能够无条件的相信交易系统?特别是有回撤的时候

另外交易系统也需要经历实战检验。没经过实战检验的交易系统,只是理论上的,必须理论联系实际,才知道交易系统的效果。才清楚什么地方需要改进,什么地方需要完善等。只有经过实战的交易系统,才知道是否能持续稳定地获利。

加油吧!稳定复利,最小回撤,做时间的朋友。

该去医院了

不仅仅是之前的早醒以及早醒后睡不着,最近这两天连续两天做噩梦,第一天的梦是回老家了,真的无论是现实还是梦中回老家,我都是精神紧张,非常疲惫。

而昨天晚上的梦更加严重,直接开始鬼压床了。

昨晚上大概10点钟开始睡觉,睡着我估计10点半,到1点15的时候第一次醒来,去上了个厕所,排量比平时多一倍,也许是水喝多了,哦对晚餐喝了一大碗小米南瓜粥,然后好像还喝了一杯水。

然后回来继续睡觉,这一次就开始做噩梦了,还是一个嵌套梦,梦中梦,梦见我躺在床上做梦,梦见我躺在床上,然后有各种类人类鬼的东西在客厅,然后我就起来去打架,后来发现是一场梦,然后嵌套的第一层的我,也发现了客厅有类人类鬼的东西,然后出去干架,发现是自己跟自己打架,然后现实的我特别想起来,睁开眼睛看一看,客厅到底有没有东西,有没有危险向我靠近,但是睁不开眼睛,也动不了,在挣扎了很久之后,终于逃脱出来,睁开了眼睛,这时候3点钟左右。

下来就不像之前那么容易睡着了,我估计挨了三四十分钟,睡着了,当然也是浅睡眠,这个时段的梦大概就是老家的事情,让我焦心,这个梦现在没啥印象了,主要还是前一个梦太吓人,太费脑子,太令人印象深刻了,这次睡着醒来大概是6点40分。

真的,这一觉睡的我真的很累,这两天的觉睡的我都很累,真的有点扛不住了,现在都能感受到心跳声,整个身体都很沉重。

不行了,真的该去医院了,好怀念能一觉睡到天亮的时候。

唉。

更稳定更小回撤的策略优化

昨天晚上突发灵感,给策略加上了一条非常简单的优化,发现对于某些标的提升十分明显:

image.png

图中蓝线代表之前策略回测结果,红线代表最新的回测结果

可以看出在某些标的上提升非常明显,其他标的或者是略有提升,或者是略有下降,但是总体上这条优化属于可以纳入的范围。

优化做了什么

其实非常简单,就是一个点:只要挣钱了,能卖就卖。

组合回测

在各标的组合回测中:

主线标的组合

仅做主线组合里,收益率似乎差异不大,但是就最近一次来说避免了大幅度的回撤

优化后

优化前

全标的组合

全标的组合两者最终收益率也是差不多,但是优化后曲线更平滑,收益更稳定,回撤更小

优化后

对比以前的曲线:

优化前

拉长时间的主线标的组合

从更长时间(18个月)跨度来看,收益率也是差不多的,主要差异还是在避免了最后一次回撤

主线标的组合优化后

主线标的组合优化前

总结

总的来说,新的优化可以平滑波动,减少回撤。主要差异在于之前策略会博一个高开,这个高开经常会有比较大的幅度,体现在收益率曲线上就是突然的大幅提升。但是如果博失败的话,就容易有大幅度的回撤。所以在行情好的时候用以前策略会拿到更好的收益,在行情不行的情况下使用这个优化能取得更稳定的收益。

那么行情好坏有一个最容易判断的方法:买入就赚还是买入就亏,买入就赚,说明行情好,可以去博弈一下,买入就亏说明行情不行,保本出。

然后提升明显的标的,那就直接应用新优化,提升不明显的标的可以应用变通的策略,行情好去博弈,行情不好保本出。

按这一思路修改之后,果然有所提升,看来以后需要灵活判断了

主线标的组合

全标的组合

拉长时间的主线标的组合

整体而言应用新优化可以更好的做到稳定盈利,最小回撤。在目前这个初始积累阶段,还是更需要资金的稳定增长。

2022年国庆

今年国庆下了一周雨,也没有出去逛

宅家做饭给宝宝们(媳妇和娃🤗)补充营养

image.png

10.5日去医院做建档和产检,抽了媳妇十一管血,完了之后还得做尿检等其他检查,真是太辛苦了,我看着都心疼。

image.png

国庆虽然没能像以往出去来个长途旅行,但是宅家的日子也过的美妙而幸福。

期待宝宝出生的那一天,还有193天。