窃花不能算偷

之前的帖子中提到了鲁迅小说《孔乙己》中茴香豆的四种写法,孔乙己这个人物让我印象深刻的还有一句话,那种就是:“窃书不能算偷……窃书!……读书人的事,能算偷么?”

image.png

说到窃书,前段时间还有人炫耀利用图书馆的规则漏洞,弄出来不少书,引发网络上的大争议,不过我今天要说的还真不是窃书,而且偷花。

别想歪,此偷花就是字面上上的偷花,而不是什么汪洋大盗采花淫贼啥的。

话说,前段时间去好远的农贸大集买了几株花,有两株月季有一盆天人菊还有一盆康乃馨还有一盆玛格丽特,为了让院子外的花园看起来不是那么空,我就将其栽种的花园里。

就像是兰花草种唱的那样:

我从山中来,带着兰花草。种在小园中,希望花开早。

我是日日勤呵护啊,早晚都要给他们浇水,有时候室外绿化水停掉,我就回屋内接自来水浇灌。就这样过了十多天,小花们总算适应了大自然的环境,看起来生机勃勃的样子。

然而有一天,我一如既往地出去浇水,结果出门一看就傻眼了,花呢?我的天人菊怎么只剩下一个大坑了呢?花却不见了!

研究大半天之后,我终于无奈地确认了一件事,那就是我的小花被人偷盗走了,没想到有人竟然丧心病狂到这种地步,到我的门口来偷花!

虽然花并不值几个钱,但是毕竟又是买来又是栽种又是辛苦照料十几天,就这样被偷走,还真让我有些郁闷。

不过郁闷的事情还在后边呢,过了三天多,我中午出去的时候另外一株康乃馨还在,下午再出去的时候,又变成了一个小土坑,是可忍熟不可忍啊。

物业管家知道后,帮我查了监控录像,可是这块恰巧是监控死角,而从录像其它部分分析来往的人,则难度大多了。

管家小妹妹为了安慰我,从物业绿化班那边给我要来好些盆小花,非要帮我栽到小花园中,于是那两个空坑和周围一小片空地都布满了小花。

加上我去年和今年春天移栽的鸢尾都已经开了起来,小花园看起来总算说得过去了。

昨天在院子里弄草莓秧的时候,看外边一家三口(应该是孩子、妈妈、姥姥),再低头欣赏我的鸢尾并拍照,我还很高兴,这也算是对我劳动成果的一种认可吧。

image.png

然而我一转身的功夫,就看她们拿着一束花走了,想追上去谴责一下,想想又作罢,毕竟对于爱花的人来讲:“窃花不能算偷……窃花!……爱花人的事,能算偷么?”


This page is synchronized from the post: ‘窃花不能算偷’

每天进步一点点:字符串(str)转字节型数据(bytes)的几种写法

还记得鲁迅小说《孔乙己》中提到的鲁迅小说《孔乙己》当中的茴香豆的“回字有四样写法”吗?孔乙己觉得自己掌握了茴香豆的几种写法,是一个有学识有文化的人。


(图源 :pixabay)

今天在写一段HMAC代码时,直接就用了

digest = hmac.new(key.encode('utf-8'), msg.encode('utf-8'), digestmod='sha256').hexdigest()

然后突然想起来我以前习惯这样用:

hmac.new(bytes(key,'utf-8') , bytes(msg, 'utf-8'), digestmod='sha256').hexdigest()

也就说说,把字符串(str)转成二进制数据/字节型数据(bytes),有两种方法,那么这两种方法有什么区别呢?

为了搞懂这个区别,我查了半天资料,然而实在觉得是没啥区别(至少在Python 3.6以后是这样),但是却被我又发现了两种转换方法。

我把它们整理一下,这样我就掌握了四种写法了,测试代码如下:

a = "苍茫的天涯是我的爱!"
print(type(a))

b = bytes(a, 'utf-8')
print(b)
print(type(b))

c = a.encode('utf-8')
print(c)
print(type(c))

d = str.encode(a, 'utf-8')
print(d)
print(type(d))

import codecs
e = codecs.encode(a, 'utf-8')
print(e)
print(type(e))

输出如下:

image.png

也就是说在python3中,无论是使用bytes(a, 'utf-8') 还是a.encode('utf-8') 还是 str.encode(a, 'utf-8') 或者codecs.encode(a, 'utf-8')都可以将字符串转换成bytes。

在测试完成后,我突然觉得我简直就是现代版的孔乙己啊,知道一种用法就行呗,非得学到四种没啥差别的用法,以后用到时,除了懵,还有啥好处呢?

当然也有可能我没掌握这几种用法真的差异和精髓,研究得不够透彻,不过我却不想研究下去了,有知道的朋友说一声吧。

当然了,顺便又重读了一遍《孔乙己》,说不出有啥感受,倒是有点伤感/(ㄒoㄒ)/~~

相关链接


This page is synchronized from the post: ‘每天进步一点点:字符串(str)转字节型数据(bytes)的几种写法’

春种一畦菜

image.png

这几天抽空把院子里大部分空地种上了,又是翻地,又是播种,有些秧苗还是开车跑了很远的地方去买回来的,累得腰酸背痛的。

先说说封面图,看这长势良好的油麦菜(请忽略后边埋汰扒拉的烧烤炉),你肯定以为这是我去年盛夏拍的吧,其实并不是。

去年夏末秋初,我看院子里这块地空着,就打算种点油麦菜,寻思即便长不大,但是只要能长出一些我就可以吃啊,结果等到冬天下雪,也没看到油麦菜的影子。

原本以为是种子不行,所以导致播种失败,没想到春天来临时,它们竟然自己长了出来,并且长得这么好,看来以后我可以考虑都在秋初种菜了呢。

这是我两天前栽的辣椒,据说有尖椒和麻椒两个品种呢:

image.png

两天前栽的西红柿,同样有大西红柿和小西红柿两个品种:

image.png

两天前栽的茄子,同样有绿茄子和紫茄子两个品种:

image.png

这是我大半个月以前播种的黄瓜,现在才长这么高,只有旱黄瓜一个品种:

image.png

想拍大片的西红柿秧苗,可是照片效果看起来不佳呢?

image.png

传说中的韭菜,已经快把第二茬割光了,据说弄个三茬还是问题不大的。

image.png

买了两根西瓜秧栽到墙边,不知道能不能结出大西瓜来?

image.png

大地草莓,秧苗五元一棵哦,就是不知道能不能结出价值五元的草莓?

image.png

草莓栽好了,希望将来能长成大片的草莓吧。

image.png

标题起了《春种一畦菜》,是想效仿《悯农》中的那句“春种一粒粟,秋收万颗子。” 在我的理解种,一畦就是一小块菜地啦,可是据说在古代,五十亩地才算一畦呢(田五十亩曰畦《说文》),真要那样的话,估计就累死我了。😳

另外《红楼梦》中贾宝玉曾作诗一首:

《杏帘在望》
杏帘招客饮,在望有山庄。
菱荇鹅儿水,桑榆燕子梁。
一畦春韭绿,十里稻花香。
盛世无饥馁,何须耕织忙。

用网络上常说的一句话:“别说了,有画面了”,哈哈哈,画面感太强烈了是不是?世外桃源一般啊。不过对于贾宝玉这样的公子哥,就应该让他多干农活,像我一样腰酸背痛,估计就没心情写诗啦。


This page is synchronized from the post: ‘春种一畦菜’

每天进步一点点:查看(get_open_orders)与取消订单(limit_order_cancel)

之前的文章中,我学习了如何创建订单,尽管有两种方法可以选择,但是最终我还是选择了一个便于理解的。但是新问题来了,订单创建后,我们如何查看?以及如何取消呢?

image.png
(图源 :pixabay)

在说到查看与取消之前,我们先来看看fill_or_kill & expiration两个参数。

fill_or_kill

fill_or_kill 的意思和字面一样,要么fill(成交),要么kill(关闭掉),也就是说我挂单出去,如果能成交就成交,如果不能成交则关闭掉。所以设置fill_or_kill = True

比如我尝试挂一个已0.888 HBD价格卖一个HIVE的订单,并设置fill_or_kill = True

image.png

我会马上得到如下返回信息:

Assert Exception:filled: Cancelling order because it was not filled.

所以fill_or_kill也可以算作取消订单的一种手段。

expiration

expiration其实更不用多说了,字面意思就是过期

有就是说我们下单的时候,可以指定这个订单什么时候过期,比如1分钟内?1个小时?3天?甚至可以是28天以内的任意一个时间点。

为什么是28天而不是30天呢?我也不懂,反正代码里这么限制的,估计和memo最大长度2048一样。

#define STEEM_MAX_LIMIT_ORDER_EXPIRATION (60*60*24*28) // 28 days

总之,expiration 可以让订单到期时自动取消,算是取消订单的另外一种手段。

查看订单

现在我们已经掌握了两种自动取消订单的手段,但是其实我们主要想要的是手动取消订单。然而说到手动取消之前,我们一定要学会如何查看订单,否则都不知道自己有哪些订单,谈何取消呢?

查看订单可以用condenser_api.get_open_orders也可以用database_api.list_limit_orders,以第一种为例,查看我自己的订单命令如下:

curl -s --data '{"jsonrpc":"2.0", "method":"condenser_api.get_open_orders", "params":["oflyhigh"], "id":1}' https://api.openhive.network

如果当前有存在的订单,那么就会返回:

{“jsonrpc”:”2.0”,”result”:[{“id”:4273895,”created”:”2020-05-12T04:03:36”,”expiration”:”2020-05-12T14:03:35”,”seller”:”oflyhigh”,”orderid”:3,”for_sale”:1000,”sell_price”:{“base”:”1.000 HIVE”,”quote”:”0.888 HBD”},”real_price”:”0.00000000000000000”,”rewarded”:false}],”id”:1}

好吧,我又被base/quote绕晕了,不过不用理会,只要知道这是一个还存在的订单就好。

除了condenser_api.get_open_orders,另外一种查看订单的方法是使用database_api.list_limit_orders,这种方法略复杂,用于查找自己的订单,大致示例如下:

curl -s --data '{"jsonrpc":"2.0", "method":"database_api.list_limit_orders", "params": {"start":["oflyhigh",0], "limit":10, "order":"by_account"}, "id":1}' https://api.openhive.network

需要注意的是,如果自己的订单不足limit的数量,它会接着输出其它人的订单,傻得不要不要的,所以还需要自己处理一下,这里就不再赘述了。

取消订单:limit_order_cancel

扯了大半天,终于到正题了,其实取消订单操作是超级简单的。

取消订单的操作大致长这样:

1
2
3
4
op_limit_order_cancel = ['limit_order_cancel',{
'owner': '',
'orderid': 0,
}]

代码大概这样:

op[1]['owner'] = owner
op[1]['orderid'] = orderid
trx.append_op(op)
trx.sign_digest(wif)
trx.broadcast()

封装成函数后调用一下,这样就把我们之前创建的指定orderid的订单取消了:

image.png

https://hiveblocks.com/ 上可以查到如下信息,证明操作是成功滴:

image.png

前尘往事

说到取消订单,想起我以前跑过一个傻傻的自动交易机器人,帮我赔了好多钱。

后来我狠心把它杀掉之后,之前遗留的订单在继续成交,持续帮我赔钱。然后我研究如何批量取消订单,费了了好大的功夫。

image.png
(图源 :pixabay)

现在再回头看,掌握了这篇文章中学到的本领,取消订单就是小CASE啦。哎,想念我可怜的交易机器人。

相关链接


This page is synchronized from the post: ‘每天进步一点点:查看(get_open_orders)与取消订单(limit_order_cancel)’

小公园走一圈

image.png

今天去招商银行办事,在附近转了两圈才把车听到招行门口,事情很快办完了,想想费这么大劲停车就这么走了,有点亏得慌,恰巧招行对面有个小公园,不如去小公园转一圈吧。

原本以外因为疫情的缘故,人会很少,可是实际上人还真不少,在亭子里乘凉聊天的,在器材那边健身运动的,还有跳广场舞的,看来大家都憋闷坏了。

公园里有一个小山,说是山,实际上还没有旁边的住宅楼高呢。不过爬上去,俯瞰整个公园,尤其是公园里的小湖和小亭子,还别有风味呢。

image.png

小山上的一个长廊,看到旁边栏杆上的小孩没?够淘气吧,不过别担心,栏杆外边还有很大一块空地,不会滚到山下去。

image.png

两座山头之间的小桥,下边是深达几米的“山涧”。

image.png

不知道谁家的小孩在玩水,真有点担心他被水流冲到山涧了,不过好在也不深,没啥大风险。

image.png

这个让我想起了《小石潭记》,不过估计小石潭记中的小石潭应该比这个大多啦。

image.png

一个年轻的妈妈带孩子玩,孩子在对岸,看起来很有意境是不是?

image.png

不得不说,淘气的孩子真多,看这个孩子都爬哪里去了?

image.png

虽然照片看起来风平浪静的,可是今天外边其实风很大的,竟然有人在打羽毛球,不得不说这技术了得啊!👍

image.png

公园里的林荫小路。

image.png

开的正艳的花,应该是蔷薇吧?为啥公园里的蔷薇花开这么早而我院子里的蔷薇还都只是叶子呢?

image.png

在公园溜达一圈后,再回到招行门口开车回家,和大自然接触了一下真好,这样总算感觉不虚此行的样子。


This page is synchronized from the post: ‘小公园走一圈’

每天进步一点点:limit_order_create 以及 limit_order_create2

之前说到Power Up,并且实现了Power UP 功能,然而Power UP需要有流动性HIVE才行,如果只有HBD,那么就需要用到内部市场来交易成HIVE了。


(图源 :pixabay)

同样,之前的交易我是用cli_wallet完成的,命令格式为:

create_order(string owner, uint32_t order_id, condenser_api::legacy_asset amount_to_sell, condenser_api::legacy_asset min_to_receive, bool fill_or_kill, uint32_t expiration, bool broadcast)

limit_order_create2

那么自己实现一个create_order功能好不好呢?于是看了一下,发现其实并不难,首先我是用 limit_order_create2来实现的,因为我总觉得所谓的2,一定是升级版本,一定比1要强。

limit_order_create2 operation大致长成这样:

1
2
3
4
5
6
7
8
9
10
op_limit_order_create2 = ['limit_order_create2',{
'owner': '',
'orderid': 0,
'amount_to_sell': {},
'exchange_rate': {
'base': '',
'quote': ''},
'fill_or_kill':True,
'expiration':''
}]

其中amount_to_sell是要卖出的资产,exchange_rate是指定价格。原本按着我之前的理解:

  • Base: 基础资产,用于计价的资产
  • Quote: 买卖资产,就是要买卖的资产

那么我指定 价格为0.340 HBD/HIVEexchange_rate部分应该是

base: 0.340 HBD
quote: 1.000 HIVE

看起来思路挺明确的,然而当我广播时,却被提示:

‘Assert Exception:amount_to_sell.symbol == exchange_rate.base.symbol: Sell ‘
‘asset must be the base of the price’

看了一下代码中的判断:

FC_ASSERT( o.amount_to_sell.symbol == o.exchange_rate.base.symbol, "Sell asset must be the base of the price" );

竟然要求销售的资产和价格中的计价资产相同,这是啥逻辑,还是我犯糊涂了。讲真,看到我发布成功的order,我都不知道自己想卖多少钱/想多少钱买了:

image.png

虽然无论用谁当作计价资产,都无所谓把价格处理一下就好,不过总觉得怪怪的,果断弃坑。

limit_order_create

既然被limit_order_create2绕晕了,觉得它不够优雅,那么还是回头看看limit_order_create吧。

limit_order_create operation大致长成这样:

1
2
3
4
5
6
7
8
op_limit_order_create = ['limit_order_create',{
'owner': '',
'orderid': 0,
'amount_to_sell': {},
'min_to_receive': {},
'fill_or_kill':True,
'expiration':''
}]

这逻辑就简单清楚多了:

amount_to_sell: 就是你要卖多少资产
min_to_receive : 就是你想到得到多少资产

这逻辑就很优雅了,好比我打算卖88个土豆,你至少给我56块钱;或者反过来,我打算出88块钱,你至少给我128个土豆。

代码也很简单,创建OP后,将对应信息填进去,然后追加到transaction中,并签名广播就好了。

填充数据:

op[1]['owner'] = owner
op[1]['amount_to_sell'] = HIVE_ASSET(amount_to_sell)
op[1]['min_to_receive'] = HIVE_ASSET(min_to_receive)
op[1]['orderid'] = orderid
op[1]['fill_or_kill'] = fill_or_kill
op[1]['expiration'] = (datetime.datetime.utcnow() + datetime.timedelta(seconds=expiration)).strftime('%Y-%m-%dT%H:%M:%S')

追加到transaction并签名广播:

trx.append_op(op)
trx.sign_digest(wif)
trx.broadcast()

发送成功的transaction

image.png

是不是和买卖土豆一样简单?

补充

在实际测试时,还发生过类似如下的错误:

‘could not insert object, most likely a uniqueness constraint was ‘
‘violated:could not insert object, most likely a uniqueness constraint was ‘
‘violated: ‘

我之前以为是我的operation填充得不对,后来才发现是因为之前已经创建成功订单,再用相同得订单ID,就会出问题了。

虽然这个提示信息不够友好,但是最终也算让我注意到订单ID重复的问题:要么管理好订单ID,要么用足够大的随机数吧。

总之,已经可以用limit_order_create来愉快地交易啦。


This page is synchronized from the post: ‘每天进步一点点:limit_order_create 以及 limit_order_create2’

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×