<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Hi, DIYgod</title><description>Coding is passion, coding to fill the world with love!</description><link>https://diygod.cc</link><item><title>A Privacy Gateway for the Whole Home: Hiding My Family&apos;s IP Behind Two NAS Boxes</title><link>https://diygod.cc/en/home-gateway</link><guid>home-gateway</guid><description>The thirty-odd devices at home all went online with our residential IP in plain view of every website, app and IoT cloud. Over one week, together with AI, I put the whole home behind a primary and a backup gateway and a self-hosted exit, and hit plenty of snags on the way: a smart bed that beeps at night, a baby camera that won&apos;t show a picture, and Bilibili videos that never stop buffering.</description><pubDate>Tue, 20 Oct 2026 04:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;&lt;video controls=&quot;&quot; playsinline=&quot;&quot; preload=&quot;metadata&quot; width=&quot;1920&quot; height=&quot;1080&quot; poster=&quot;/assets/videos/home-gateway-intro-en.jpg&quot; src=&quot;/assets/videos/home-gateway-intro-en.mp4&quot;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h2 id=&quot;why&quot;&gt;Why&lt;/h2&gt;
&lt;p&gt;I’ve long wanted my home network to be a bit more invisible. I don’t want every website, app and IoT cloud to see our residential IP and work out from it roughly where we live.&lt;/p&gt;
&lt;p&gt;So far I had done very little about it. We have a MacBook Pro at home that runs with its lid closed as a server, and it ran Surge, but only my own computer used it; AdGuard Home handled our DNS. The other thirty-odd devices all went online directly: my family’s phones and tablets, computers, a projector, cameras, smart beds, a pet feeder. Even the electric toothbrush talks to the internet on its own.&lt;/p&gt;
&lt;p&gt;Early this year I did try letting Surge take over the whole home, but one Mac as the only gateway wasn’t stable enough, and I rolled it back before long. This time I wanted to do it properly, with just two requirements: apart from the LAN, no traffic may leave with the residential IP, and no single machine failing may take the home offline.&lt;/p&gt;
&lt;p&gt;It took about a week, and almost all of it was a chat with Claude Code. I brought the requirements and made the calls; it ran the speed tests, wrote the configs, deployed them and did the troubleshooting. Below I go through the final setup piece by piece, along with the snags worth knowing about.&lt;/p&gt;
&lt;h2 id=&quot;the-overall-setup&quot;&gt;The overall setup&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/overview-en.BBP6is86_Z1BIBuO.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2000&quot; height=&quot;1280&quot; alt=&quot;The overall setup: every device at home goes to the gateway at 192.168.5.2, with Surge on the Mac as the primary and mihomo on a mini PC as the backup; traffic then leaves through the Singapore, Hong Kong or Shenzhen exit, and a few exceptions go direct through the router&quot;&gt;&lt;/p&gt;
&lt;p&gt;None of the devices needs any setup. The router’s DHCP hands out 192.168.5.2 as the gateway and 198.18.0.2 as DNS, so a device is covered as soon as it joins the Wi-Fi.&lt;/p&gt;
&lt;p&gt;Normally that gateway address belongs to Surge on the Mac. A mini PC running fnOS runs mihomo as the backup: when Surge stops answering, it takes the address over, and hands it back once Surge returns. If both are down, the home simply goes offline instead of quietly going out through the router.&lt;/p&gt;
&lt;p&gt;There are three exits. Most traffic leaves through a Linode server I run myself in Singapore (Egress SG). Traffic to mainland China addresses goes through my proxy provider’s Hong Kong lines (Egress HK). Services that insist on a mainland address, such as Quark, Ximalaya and Bilibili, go on from Hong Kong to an Alibaba Cloud server in Shenzhen (Egress CN).&lt;/p&gt;
&lt;p&gt;The configs of both gateways come from one repository and deploy themselves every morning. The gateway also logs every request, and every morning AI reviews them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-gateway-an-address-that-can-move&quot;&gt;The gateway: an address that can move&lt;/h2&gt;
&lt;p&gt;At first I wanted to do it all on the router, but our TP-Link has no SSH, no USB port and can’t run OpenWrt, so nothing can be installed on it. Without replacing the router, the only option was a “side gateway”: the router keeps handing out addresses as usual, but the gateway it announces points at another machine at home.&lt;/p&gt;
&lt;p&gt;What a side gateway fears most is being a single point of failure. DHCP takes only one gateway address, and even if it took several, phones and laptops wouldn’t switch between them on their own, so the gateway address itself has to move between machines. Surge’s VM gateway holds an IP on the LAN by itself, with its own MAC address, and I pinned it to 192.168.5.2. On the backup mini PC, keepalived watches that address. Its config comes down to two checks: &lt;code&gt;arping -D&lt;/code&gt; asks whether anyone else on the LAN still answers for .2, and a second check asks whether mihomo itself is healthy. It takes over only when both pass.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/failover-en.D1cstMTz_ZlsoBY.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2000&quot; height=&quot;960&quot; alt=&quot;Normally 192.168.5.2 belongs to Surge on the Mac while the mini PC watches it with arping; when Surge is gone, the mini PC takes the address over in about 10 seconds and hands it back when Surge returns&quot;&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;vrrp_script surge_absent {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  # arping -D exits 0 only when no other host answers for the address.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  script &quot;/usr/sbin/arping -D -q -c 2 -w 2 -I eth0 192.168.5.2&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  interval 3&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  rise 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  fall 2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;vrrp_instance gateway {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  state BACKUP&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  interface eth0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  virtual_router_id 52&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  virtual_ipaddress {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    192.168.5.2/24 dev eth0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  track_script {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    surge_absent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    mihomo_ready&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;keepalived doesn’t run on macOS, so the failover logic has to live on the Linux side. The whole backup gateway sits in a network namespace of its own and gets its own MAC and IP through macvlan, so the host’s routing and firewall don’t change at all. A failover takes about 10 seconds, and open connections are reset.&lt;/p&gt;
&lt;p&gt;The two gateways run two different engines, but there is only one config to maintain: the Surge profile is generated first and then converted into a mihomo config automatically. The converter takes care of the differences in syntax, such as logical rules, rule sets, proxy chains and empty DNS answers, and tests make sure both gateways treat the same request the same way.&lt;/p&gt;
&lt;p&gt;One thing worth knowing about monitoring: when the primary gateway breaks, Surge’s process and API may still look perfectly fine while the gateway’s virtual interface has simply stopped receiving packets. So in Uptime Kuma I check whether LAN devices actually have connections passing through Surge, not just whether its API answers.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-exits&quot;&gt;The exits&lt;/h2&gt;
&lt;h3 id=&quot;warp-was-too-slow&quot;&gt;WARP was too slow&lt;/h3&gt;
&lt;p&gt;The plan was to use Cloudflare WARP as the first exit. Functionally it was fine: the residential IP was hidden, and the exit became a shared Cloudflare IP. On our broadband, though, the speed was hopeless:&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Test server (all in Singapore)&lt;/th&gt;&lt;th&gt;Direct&lt;/th&gt;&lt;th&gt;Through WARP&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Linode&lt;/td&gt;&lt;td&gt;798–885 Mbps&lt;/td&gt;&lt;td&gt;124–146 Mbps&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hetzner&lt;/td&gt;&lt;td&gt;327–706 Mbps&lt;/td&gt;&lt;td&gt;5–16 Mbps&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The problem is the ISP: from home, Cloudflare Singapore sits behind a detour over an international carrier, at around 40 ms, while Linode and Hetzner are only 4–9 ms away, and even a plain download from Cloudflare without WARP managed only a little over 200 Mbps. No amount of local tuning fixes that. WARP also hides the IP but not the country: connect from Singapore, and the exit still shows Singapore. If your own broadband has a poor route to Cloudflare, WARP isn’t a good exit for the whole home.&lt;/p&gt;
&lt;h3 id=&quot;a-self-hosted-vps&quot;&gt;A self-hosted VPS&lt;/h3&gt;
&lt;p&gt;A proxy provider’s nodes aren’t a good fit either. Before I had picked a VPS, I used one as a temporary exit for the whole home, and that same evening the home went offline: each node allows a single user only about 256 concurrent connections, and with the whole family’s connections on one node, it filled up quickly.&lt;/p&gt;
&lt;p&gt;What matters most in a VPS is the route from home to its datacenter, and even within Singapore that can differ several times over, so it’s best to test directly from home. These are my measurements at the evening peak:&lt;/p&gt;







































































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Provider · region&lt;/th&gt;&lt;th&gt;Latency (median)&lt;/th&gt;&lt;th&gt;Download&lt;/th&gt;&lt;th&gt;Notes&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Linode Singapore&lt;/td&gt;&lt;td&gt;8.4 ms&lt;/td&gt;&lt;td&gt;660–896 Mbps&lt;/td&gt;&lt;td&gt;$5/month with 1 TB, then $0.005/GB&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hetzner Singapore&lt;/td&gt;&lt;td&gt;8.3 ms&lt;/td&gt;&lt;td&gt;612–899 Mbps&lt;/td&gt;&lt;td&gt;Only 0.5 TB included&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Vultr Singapore&lt;/td&gt;&lt;td&gt;9.3 ms&lt;/td&gt;&lt;td&gt;About 200 Mbps per connection&lt;/td&gt;&lt;td&gt;700 Mbps only in parallel&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Tencent Cloud Singapore&lt;/td&gt;&lt;td&gt;7.4 ms&lt;/td&gt;&lt;td&gt;No test file&lt;/td&gt;&lt;td&gt;Razor Speed plan: $5 with unlimited traffic, capped at 200 Mbps&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;DigitalOcean Singapore&lt;/td&gt;&lt;td&gt;40.8 ms&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;Detours through Tata&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OVH Singapore&lt;/td&gt;&lt;td&gt;57 ms&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;Detours through PCCW&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Huawei Cloud Singapore&lt;/td&gt;&lt;td&gt;187 ms&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;Detours inside Huawei’s own network&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;AWS Kuala Lumpur&lt;/td&gt;&lt;td&gt;13.3 ms&lt;/td&gt;&lt;td&gt;–&lt;/td&gt;&lt;td&gt;Traffic is expensive&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Alibaba Cloud Kuala Lumpur&lt;/td&gt;&lt;td&gt;47.8 ms&lt;/td&gt;&lt;td&gt;347 Mbps&lt;/td&gt;&lt;td&gt;Detours inside Alibaba Cloud&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Linode Jakarta&lt;/td&gt;&lt;td&gt;20–65 ms&lt;/td&gt;&lt;td&gt;17–115 Mbps&lt;/td&gt;&lt;td&gt;Unstable&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;I went with Linode’s cheapest plan in Singapore, the Nanode 1 GB, at $5 a month. Compared with the proxy provider’s nodes, time to first byte fell from 230–500 ms to 37–52 ms. The exit shows Singapore, though: the residential IP is hidden, the country isn’t.&lt;/p&gt;
&lt;h3 id=&quot;wireguard-or-ss2022&quot;&gt;WireGuard or SS2022&lt;/h3&gt;
&lt;p&gt;The protocol started out as WireGuard: it’s built into the kernel and needs nothing else installed on the server, so it looked like the least hassle. In practice, SS2022 turned out better:&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;4 parallel connections, two rounds&lt;/th&gt;&lt;th&gt;WireGuard&lt;/th&gt;&lt;th&gt;SS2022&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Throughput&lt;/td&gt;&lt;td&gt;1036–1141 Mbps&lt;/td&gt;&lt;td&gt;1217–1246 Mbps&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Server CPU&lt;/td&gt;&lt;td&gt;73–80%&lt;/td&gt;&lt;td&gt;39–45%&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Time to first byte&lt;/td&gt;&lt;td&gt;32–35 ms&lt;/td&gt;&lt;td&gt;32–35 ms&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;WireGuard encrypts, forwards and NATs every packet of around 1.4 KB on its own, while SS2022 runs over TCP, which gets the NIC’s segmentation offloads and hardware AES, so the same traffic costs half the CPU. On a small VPS with a single core, that headroom matters. The server now runs only SS2022 on &lt;a href=&quot;https://github.com/SagerNet/sing-box&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;sing-box&lt;/a&gt;, and both gateways come close to filling the home’s 1.1–1.2 Gbps line.&lt;/p&gt;
&lt;p&gt;This exit also blocks QUIC, and browsers fall back to TCP by themselves. Surge picks up every device’s TCP connection inside the LAN, so packets lost on Wi-Fi are recovered right there, which makes TCP no worse than QUIC here, and it saves the server some CPU. And since I don’t want the network dropping all the time, nothing that interrupts connections is allowed to update itself on the server; sing-box is only upgraded by hand.&lt;/p&gt;
&lt;h3 id=&quot;reaching-mainland-china&quot;&gt;Reaching mainland China&lt;/h3&gt;
&lt;p&gt;Many Chinese sites have CDNs in Singapore and are only tens of milliseconds away through Linode. What needs separate handling is two kinds of traffic: traffic to servers that only exist in the mainland, and services that must see a mainland address.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/mainland-en.Bdl5c6wh_ZdYsLn.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2000&quot; height=&quot;1000&quot; alt=&quot;Three lines: sites abroad go through Linode in Singapore; mainland-only servers go through the Hong Kong lines at 104–148 ms round trip at the evening peak; services that need a mainland IP continue from Hong Kong to Alibaba Cloud in Shenzhen. Straight from home, Shenzhen took 330–440 ms with packet loss&quot;&gt;&lt;/p&gt;
&lt;p&gt;I already had an Alibaba Cloud server in Shenzhen, but a round trip from home took 330–440 ms with packet loss, stuck at the international entry into the mainland. Luckily my proxy provider has a few premium Hong Kong lines that reach the mainland in 104–148 ms round trip at the evening peak. So traffic to mainland addresses goes through the Hong Kong lines, and services that need a mainland address are chained on through Hong Kong to the Alibaba Cloud server. The config looks roughly like this, with the node names simplified:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;[Proxy Group]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;Egress &lt;/span&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;SG&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; = fallback, Linode Singapore, Provider&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&apos;s Singapore nodes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;Egress HK = fallback, Hong Kong line 1, Hong Kong line 2, Other Hong Kong nodes, url=http://oss-cn-shenzhen.aliyuncs.com/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;Egress CN = fallback, Alibaba Cloud Shenzhen, Egress HK, Egress SG&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;[Rule]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;DOMAIN-SUFFIX,cn,Egress HK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;GEOIP,CN,Egress HK&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;FINAL,Egress SG&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Alibaba Cloud server runs AnyTLS on sing-box, and in Surge &lt;code&gt;underlying-proxy = Egress HK&lt;/code&gt; chains it behind the Hong Kong exit; on mihomo the equivalent is &lt;code&gt;dialer-proxy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;There are a few Surge traps here. Use fallback rather than url-test for these groups: url-test changed node about ten times a day, and the chain behind it slowed down for a while after every change, whereas fallback only moves down the list when the current node fails its test. Surge also ignores &lt;code&gt;url=&lt;/code&gt; on a group and tests each node with that node’s own &lt;code&gt;test-url&lt;/code&gt;, falling back to the global address when there is none, so if you want nodes timed by their latency to the mainland, each node needs its own &lt;code&gt;test-url&lt;/code&gt;. Finally, the global 3-second test timeout is too tight for cross-border lines: a single slow answer at the evening peak is enough to mark a node failed, so I gave the cross-border nodes 8 seconds of their own.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;plugging-the-leaks&quot;&gt;Plugging the leaks&lt;/h2&gt;
&lt;p&gt;Once the gateway was live, the real work was hunting down the traffic that quietly went around it.&lt;/p&gt;
&lt;p&gt;First the basics: &lt;code&gt;hijack-dns = *:53&lt;/code&gt; takes over every DNS query crossing the gateway, whatever DNS server a device has set for itself, and ordinary names get fake IPs, which lets rules match by domain. UDP that a node can’t relay is dropped rather than sent direct, and ICMP isn’t forwarded, so pings don’t leave with the residential IP. The hostnames of the proxy servers are resolved over DoH in advance and written into the config as IPs, so the gateway doesn’t depend on its own DNS to find them.&lt;/p&gt;
&lt;p&gt;The sneakiest one is Surge’s “UDP fast path”. After a device sends UDP to 40 high ports within one second, its exit turns into the residential IP. It turns out Surge automatically downgrades “devices with many UDP connections” to plain forwarding, which bypasses every rule, and P2P, some games and group video calls can all trigger it. Surge has no global switch for this; you can only untick “Allow UDP Fast Path” one device at a time in the device list, and it is on by default for new devices. This one still isn’t fully solved.&lt;/p&gt;
&lt;p&gt;iCloud Private Relay is another trap. While it’s on for the home Wi-Fi, Apple devices send their DNS queries, encrypted, to Private Relay’s resolvers, so the gateway sees nothing but bare IPs, and every domain rule and the ad block stop working. Fortunately Apple documents a proper way for network administrators to turn it off: if the network’s DNS answers &lt;code&gt;mask.icloud.com&lt;/code&gt; and &lt;code&gt;mask-h2.icloud.com&lt;/code&gt; with nothing, Apple devices stop using Private Relay on that network. In Surge it takes two rules, where &lt;code&gt;pre-matching&lt;/code&gt; makes it answer NOERROR with no records right at the DNS stage:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;DOMAIN,mask.icloud.com,REJECT,pre-matching&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;DOMAIN,mask-h2.icloud.com,REJECT,pre-matching&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/relay-en.BMP4R1rO_2bptx0.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2000&quot; height=&quot;800&quot; alt=&quot;With Private Relay on, DNS goes encrypted to Private Relay&apos;s resolvers and the gateway sees only IPs; once the gateway gives an empty answer for mask.icloud.com, Apple devices turn Private Relay off on this network and the gateway sees domains again&quot;&gt;&lt;/p&gt;
&lt;p&gt;Afterwards, the share of my MacBook’s requests with only an IP and no domain fell from 70–82% to 7–9%, and Private Relay’s QUIC retries stopped as well. Away from home, Private Relay works as usual.&lt;/p&gt;
&lt;p&gt;Also, turn off “Private Wi-Fi Address” for the home network on the phones and tablets you use most, so the gateway can recognize them by a fixed MAC. And some devices keep their old gateway settings: our pet feeder didn’t pass through the gateway for a whole week after the switch, until a power cycle brought it back.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;traffic-that-should-go-direct&quot;&gt;Traffic that should go direct&lt;/h2&gt;
&lt;p&gt;Not all traffic is suited to a proxy, either. Some services won’t accept a datacenter IP, some traffic is simply too heavy, and some tasks should use the residential IP in the first place. In the end, all of these leave straight from home.&lt;/p&gt;
&lt;h3 id=&quot;services-that-wont-accept-a-datacenter-ip&quot;&gt;Services that won’t accept a datacenter IP&lt;/h3&gt;
&lt;p&gt;The iPhones at home kept saying “Unable to Check for Update” on Wi-Fi, and were fine as soon as they switched to cellular. It turned out Apple answered every update check from the Linode address with the same rejection, so the update check, the signing before an install and the update downloads now all go direct.&lt;/p&gt;
&lt;p&gt;Taobao was the same. Behind a datacenter IP, signing in and opening pages kept running into slider challenges, so Taobao, Tmall and their CDNs had to go direct too. The direct rules come after the ad block, though, so Alibaba’s trackers and device-fingerprinting hosts stay blocked.&lt;/p&gt;
&lt;h3 id=&quot;traffic-thats-too-heavy&quot;&gt;Traffic that’s too heavy&lt;/h3&gt;
&lt;p&gt;The Nanode includes only 1 TB of outbound traffic a month, and since it relays everything, both the home’s uploads and downloads count as outbound there. The first big item was Home Assistant’s speed test integration: it tested every hour, about 350 GB a month, and with the whole home behind the gateway it no longer measured our broadband anyway, so I turned it off right away.&lt;/p&gt;
&lt;p&gt;Even then it used 70–77 GB a day, more than twice the allowance, and this time the biggest item was Apple: the macOS content cache on the Mac uploads 15–21 GB a day to iCloud on behalf of the whole family, on top of downloads from the App Store and Apple’s CDN. The overage isn’t actually expensive, just $0.005 per GB, but to save Linode traffic I still let iCloud uploads, App Store downloads and Apple’s CDN go direct. iCloud also keeps part of its storage with Google Cloud and Amazon S3, and one iPhone sent 17.5 GB to Google in a single day, so these go direct too, with &lt;code&gt;DOMAIN-WILDCARD&lt;/code&gt; matching only Apple’s bucket names on S3:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;DOMAIN-SUFFIX,content-storage-upload.googleapis.com,DIRECT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;DOMAIN-SUFFIX,content-storage-download.googleapis.com,DIRECT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;DOMAIN-WILDCARD,??-???-000??.s3.dualstack.*.amazonaws.com,DIRECT&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;tasks-that-should-use-the-residential-ip&quot;&gt;Tasks that should use the residential IP&lt;/h3&gt;
&lt;p&gt;Cloud drive backups are a typical case: GuangYa throttles download links issued to a datacenter IP to about 35 KiB/s, and after a burst of uploads, Quark simply started refusing TLS handshakes from a datacenter IP. Downloading iCloud photos falls into this group too.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/direct-en.Dul5xySl_1Mbdae.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2000&quot; height=&quot;840&quot; alt=&quot;On the Synology and the mini PC, ordinary containers follow the default route through the gateway and leave with the exit server&apos;s IP; containers on the direct network (172.30.0.0/24) use routing table 100 and go straight out through the router with the residential IP&quot;&gt;&lt;/p&gt;
&lt;p&gt;These tasks all run in Docker, so on both the Synology and the backup mini PC I created a Docker network called &lt;code&gt;direct&lt;/code&gt; with a fixed subnet (172.30.0.0/24), sent straight to the router with source-based policy routing:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;100: from 172.30.0.0/24 lookup main suppress_prefixlength 0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;101: from 172.30.0.0/24 lookup 100   # table 100 holds one route: default via 192.168.5.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first rule looks up the main table while ignoring its default route, so the LAN and other containers stay reachable as usual; packets for the internet fall through to the second rule and leave straight through the router. To send a container direct, add the network to its compose file:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;yaml&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;services&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  app&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    networks&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;      - &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;direct&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;networks&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  direct&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    external&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Watch out, though: some NAS systems rewrite routing rules at boot or whenever you change their network settings. fnOS, for one, wipes rules kept in NetworkManager, and in the end I needed a dispatcher script to put them back.&lt;/p&gt;
&lt;p&gt;Containers on the Mac are another story. All OrbStack containers look like a single process to Surge, so there is no telling them apart. Surge therefore has a direct entrance bound only to the loopback address, &lt;code&gt;127.0.0.1:6160&lt;/code&gt;, with the rule &lt;code&gt;IN-PORT,6160,DIRECT&lt;/code&gt;, and a container that needs to go direct points &lt;code&gt;HTTP_PROXY&lt;/code&gt; at &lt;code&gt;host.docker.internal:6160&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ads-and-telemetry&quot;&gt;Ads and telemetry&lt;/h2&gt;
&lt;p&gt;Once devices ask the gateway for DNS, their queries no longer reach AdGuard Home, so filtering had to move to the gateway as well. I turned the filter lists AdGuard Home used into Surge domain sets, and into rule sets on the mihomo side.&lt;/p&gt;
&lt;p&gt;What made me take telemetry seriously was the review finding that my MacBook had sent four to five hundred MB to &lt;code&gt;sentry.netease.com&lt;/code&gt; in a single day. It was the Mac host of NetEase’s UU Remote, which attaches two log files of 10 MB each to every Sentry event and sends one every time the iPad connects. After blocking it, I looked for privacy and telemetry blocklists that other people maintain. The method: take the 1,591 hosts the home actually reached over two days, according to the gateway log, and see what each list would &lt;strong&gt;newly&lt;/strong&gt; block:&lt;/p&gt;













































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;List&lt;/th&gt;&lt;th&gt;Newly blocked&lt;/th&gt;&lt;th&gt;Issues&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;HaGeZi Light / Normal&lt;/td&gt;&lt;td&gt;93 / 94&lt;/td&gt;&lt;td&gt;Don’t block crash reporting by design&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;HaGeZi Pro&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;&lt;strong&gt;131&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;A few hosts need allowing&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HaGeZi Pro++&lt;/td&gt;&lt;td&gt;174&lt;/td&gt;&lt;td&gt;Blocks Xiaomi’s cloud, Find My Device and Douyin’s main API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;HaGeZi Ultimate&lt;/td&gt;&lt;td&gt;198&lt;/td&gt;&lt;td&gt;Blocks ipinfo.io, which the exit checks use&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;OISD Small / Big&lt;/td&gt;&lt;td&gt;21 / 46&lt;/td&gt;&lt;td&gt;Very conservative, doesn’t even block Sentry&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1Hosts Lite&lt;/td&gt;&lt;td&gt;128&lt;/td&gt;&lt;td&gt;Blocks the Mi Home API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;anti-AD / AdRules&lt;/td&gt;&lt;td&gt;71 / 60&lt;/td&gt;&lt;td&gt;Mostly a subset of Pro&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;What I use now is the AdGuard DNS filter, uBlock Badware and &lt;a href=&quot;https://github.com/hagezi/dns-blocklists&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;HaGeZi&lt;/a&gt; Pro, plus HaGeZi’s threat intelligence list TIF medium, about a million entries together. Threat intelligence is only useful while it’s fresh, so it deploys every morning.&lt;/p&gt;
&lt;p&gt;Lists this big come with two Surge traps. First, a domain set holds at most a million entries, and beyond that Surge refuses to load it, without the profile check noticing; the first time I loaded the full TIF, the primary gateway blocked nothing at all for 3 whole minutes. Second, a Surge reload only applies the sections that changed, and domain sets are cached by path, so if you only update the lists without touching the profile, the new lists never take effect; each version needs a file name of its own.&lt;/p&gt;
&lt;p&gt;Blocking more inevitably breaks things now and then. The Doubao input method downloads its speech recognition settings from &lt;code&gt;is.snssdk.com&lt;/code&gt;, and without them it silently ignores the voice shortcut, not even showing an error; shared Taobao live-stream pages wouldn’t open either. When that happens, allow just the one host that’s needed, and the analytics and device-fingerprinting hosts behind it stay blocked.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;chinese-services-and-smart-home-devices&quot;&gt;Chinese services and smart home devices&lt;/h2&gt;
&lt;h3 id=&quot;bilibili&quot;&gt;Bilibili&lt;/h3&gt;
&lt;p&gt;Seeing a Singapore IP, Bilibili serves video from its overseas mirrors, where a video that isn’t cached manages only 3–10 Mbps per connection, nowhere near enough for high quality. With a mainland address it hands out its mainland CDN and PCDN, measured at 30–80 Mbps and steady even for obscure videos. Sending just &lt;code&gt;api.bilibili.com&lt;/code&gt; through the mainland isn’t enough: when you open a video page, the play URLs come embedded in the page itself, and Bilibili picks the CDN by the address that requested the page, so both its web pages and its app APIs need to go through Egress CN.&lt;/p&gt;
&lt;p&gt;Watching live streams, I also ran into my proxy provider’s “fair use” throttle. The cause was the live player’s P2P, which sends a burst of over two hundred STUN requests to &lt;code&gt;stun.chat.bilibili.com&lt;/code&gt; every 20 seconds. Going through the Alibaba Cloud chain, each request opened a new connection on the provider’s side, and short connections popping up in batches like that probably looked like running P2P. Yet the P2P was doing almost nothing, and the picture came from the CDN, so I simply refuse Bilibili’s STUN servers at the DNS stage, which leaves the danmaku alone:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ini&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;AND,((DOMAIN-SUFFIX,chat.bilibili.com),(DOMAIN-KEYWORD,stun)),REJECT,pre-matching&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;smart-home-devices&quot;&gt;Smart home devices&lt;/h3&gt;
&lt;p&gt;Smart home devices are another kind of headache: most of them can’t live without their vendor’s cloud, and they care a lot about which address they connect from.&lt;/p&gt;
&lt;p&gt;Our Haima Baby (海马爸比) baby camera streams through Tencent Cloud IoT’s P2P and relay, which needs the camera and the phone to reach it through the same exit. With the gateway in place, their traffic was split across the Hong Kong and Singapore exits, and the relay never came together. There was a sneakier problem too: the app caches the addresses it resolves forever, while Surge’s fake IPs go stale after a config reload, so the app kept sending packets to dead addresses until I deleted and reinstalled it. Hostnames like these are best put in &lt;code&gt;always-real-ip&lt;/code&gt;, so they get real addresses. Later the Hong Kong lines started giving the two ends different exit addresses, and in the end I let the camera go direct, at the cost of Tencent and the vendor seeing our residential IP.&lt;/p&gt;
&lt;p&gt;The smart bed, meanwhile, beeped every now and then at night, because it resets itself whenever it loses its connection. The gateway log showed that after I sent mainland addresses through Hong Kong, Xiaomi’s dispatcher saw the request come from Hong Kong and assigned the bed a server in Beijing, so its long-lived connection had to cross the border and kept dropping in the small hours. Sending the dispatcher through Singapore didn’t help, since it hands out Beijing wherever it is asked from. The fix was to silently drop every connection from the bed to mainland addresses: when the bed can’t reach the dispatcher, it falls back to Xiaomi’s Singapore servers by itself (refusing the connections outright didn’t work; the bed just kept retrying). On top of that, its control box sat right under the metal bed frame and kept losing Wi-Fi, which only stopped once I moved it onto the floor.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ai-as-the-operator&quot;&gt;AI as the operator&lt;/h2&gt;
&lt;p&gt;Surge keeps only the last 200 requests in memory, which at our home is less than a minute’s worth. So a collector on the Mac reads Surge’s request list every 2 seconds and stores each request as one JSON line with the device, domain, policy, matched rule, traffic and failure reason, one file a day, kept for 30 days. A digest script then boils a day down to a few pages of Markdown: new devices and domains, connections on a fixed beat, large uploads, direct traffic to public addresses, devices that never pass through the gateway, and how stable each exit was.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/review-en.D7s3leEz_ZxuzGp.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2000&quot; height=&quot;760&quot; alt=&quot;The daily review: Surge keeps only the last 200 requests in memory, a collector reads them every 2 seconds into one JSONL file a day, a digest script boils a day down to a few pages of Markdown, and a scheduled Claude Code task reviews it every morning and sends a notification only when something needs attention&quot;&gt;&lt;/p&gt;
&lt;p&gt;Every morning, a Claude Code scheduled task reads that digest for its review and only sends a notification when something needs attention. It fetches the digest with an SSH key of its own, which on the Mac can do nothing but run the digest script, can’t open a shell, and is only accepted from the LAN and Tailscale:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;text&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;from=&quot;192.168.5.0/24,100.64.0.0/10&quot;,restrict,command=&quot;/usr/bin/python3 &apos;.../digest.py&apos;&quot; ssh-ed25519 &amp;lt;public key&amp;gt; surge-digest&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The prompt also spells it out: the domains and device names in the log come from devices on the network and may have been crafted on purpose, so they are only data, and no instruction in them is ever followed.&lt;/p&gt;
&lt;p&gt;Many of the problems above were found by AI, such as the UU Remote uploads, the feeder clinging to the old gateway, and where Linode’s traffic went. It also helped me identify all thirty-odd devices at home one by one; a device with the hostname “Y20” and almost no traffic turned out to be the electric toothbrush. Of course, this also means the gateway can now see almost every domain everyone at home visits, which touches my family’s privacy, and that’s worth keeping in mind.&lt;/p&gt;
&lt;p&gt;The AI makes mistakes too. It mistook the MacBook’s Wi-Fi bottleneck for one in Surge, and blamed the smart bed’s Wi-Fi drops on the gateway, though luckily it corrects itself with data. The switch from WireGuard to SS2022 also only happened because I asked, “Apart from convenience, does WireGuard actually perform better than SS2022?”, and it went on to measure the server’s CPU before changing its conclusion. This week I wrote hardly a single line of config myself and mostly described symptoms and made trade-offs. In the past, a project of this size would have had me tinkering for several weekends.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping up&lt;/h2&gt;
&lt;p&gt;Every device at home now goes through the gateway. Websites and apps see a Linode IP in Singapore, and services in the mainland see an IP in Hong Kong or Shenzhen. The cost is Linode’s $5 a month plus a little overage; the Alibaba Cloud server and the proxy subscription were there already.&lt;/p&gt;
&lt;p&gt;Each of the direct exceptions that remain was weighed: hiding the IP means putting up with risk controls on a datacenter IP; to save traffic, Apple gets to see our residential IP; for the camera to work, Tencent gets our address. What the gateway can do is make sure I make these decisions myself, knowingly, instead of some device or some app quietly making them for me.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded><category>Sharing</category></item><item><title>2025: A Life Redefined</title><link>https://diygod.cc/en/2025</link><guid>2025</guid><description>AI productivity revolution and uncertainty, welcoming new life, making money, outdated open-source mindset, cyber life</description><pubDate>Thu, 25 Dec 2025 04:00:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;ai-productivity-revolution-and-uncertainty&quot;&gt;AI Productivity Revolution and Uncertainty&lt;/h2&gt;
&lt;p&gt;Four years ago, I was preparing for graduate school. At that time, I was very interested in image and video processing, so I initially wanted to study computer vision. Later, I discovered that traditional CV had become a sunset industry, completely outclassed by deep learning, so I simply chose Artificial Intelligence instead.&lt;/p&gt;
&lt;p&gt;Back then, I was just skimming through concepts. I never expected that in less than two years, AI would completely explode. In March 2023, GPT-4 was released, shocking the entire world. This year, AI has gone even further, starting to genuinely transform how many industries work and how many people live.&lt;/p&gt;
&lt;p&gt;As a programmer and illustration enthusiast, I felt the impact from both directions almost simultaneously. On one hand, there’s excitement from the massive efficiency gains; on the other hand, there’s growing confusion that has evolved into an existential crisis. This isn’t some light anxiety—it’s suddenly realizing that the very foundation you’ve built your life on is being rewritten, and it’s happening very fast.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Illustration: From Years of Practice to Typing a Sentence&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Original artists were probably among the first to be impacted. When Stable Diffusion first appeared, the artist community fiercely resisted. What’s terrifying is that their voices faded away soon after. It wasn’t that they compromised—AI-generated work was just too good, too fast. So fast that many debates became obsolete before they could even happen.&lt;/p&gt;
&lt;p&gt;Afterward, some original artists began embracing AI as an assistive tool, while a flood of newcomers entered the field, joining the creative process with lower barriers and higher output.&lt;/p&gt;
&lt;p&gt;The barrier to creation kept dropping—from years of learning and practice, to buying a good graphics card and tinkering for a few days, to now just opening a webpage, typing your idea, and waiting a few seconds. Taking it further, the path from illustration to animation to video is being rapidly connected. In some sense, “everyone is an artist” is no longer an exaggeration but more like reality unfolding before our eyes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Programming: From Code Completion to Taking Over Projects&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1945335801266721237&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1945335801266721237.BDfbR04k_m7bsz.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;1072&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The changes on the programmer side might lead to the same outcome, just less immediate and intuitive. In 2022, people were still using GitHub Copilot for auto-completion. This year, specialized AI coding models and vibe coding tools have emerged one after another. In many cases, you describe your requirements in natural language, and AI writes code, modifies code, fixes bugs, adds tests, and can even produce a complete project.&lt;/p&gt;
&lt;p&gt;For programmers, vibe coding can multiply productivity several times over. For non-programmers, coding is no longer an exclusive skill for programmers. More and more people can create polished applications without writing a single line of code, solving their real needs. Programming is transforming from a professional skill into a form of expression that everyone can engage with.&lt;/p&gt;
&lt;p&gt;As a result, how programmers work is being rewritten. The traditional “code craftsman” who writes code by hand will become increasingly rare. Many areas will be replaced by AI, just like traditional CV was. I’m also writing less and less code by hand, growing accustomed to communicating with the AI that’s always within reach on the right side of my editor. It completes tasks quickly and well, and keeps getting better—from tool to assistant, to something that almost feels like it’s replacing my own self.&lt;/p&gt;
&lt;p&gt;The most painful part of this replacement is that it’s not just replacing manual labor. It’s replacing judgment, patterns, experience, and proficiency. It’s also replacing the learning speed I used to be proud of. The knowledge I’ve acquired is depreciating. The experience I’ve accumulated is depreciating. The skills I possess are depreciating. Even intelligence and creativity are depreciating, and the depreciation rate seems far higher than my growth rate. After programmer positions truly disappear, where will my value lie?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1945354030839898431&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1945354030839898431.N1LnmvXF_27vjTW.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1100&quot; height=&quot;660&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;But clearly, programmers cannot be completely replaced right now. Perhaps AI will hit a wall at some point, like Moore’s Law. What I can do now is harness this force well, actively participate in this productivity revolution, and hopefully find my place and irreplaceable unique value within it.&lt;/p&gt;
&lt;h2 id=&quot;welcoming-new-life&quot;&gt;Welcoming New Life&lt;/h2&gt;
&lt;p&gt;No matter how AI develops, the process of carbon-based life giving birth to new life cannot be replaced by silicon-based life in the foreseeable future.&lt;/p&gt;
&lt;p&gt;The moment I learned that my childhood friend (my wife) was pregnant, I mostly felt at a loss. But from that moment on, many psychological changes and lifestyle habits quietly started revolving around this soon-to-arrive new life.&lt;/p&gt;
&lt;p&gt;My wife’s pregnancy was long and grueling. Early on, morning sickness was overwhelming, her sense of smell became extremely sensitive, normally tolerable odors became unbearable, foods she loved couldn’t be eaten, and hormones made emotional swings more pronounced. The middle period was better, but toward the end, her belly grew larger and larger, bringing back pain, declining sleep quality, and difficulty moving—even walking became a problem. Yet she showed remarkable resilience and optimism, enduring these challenges while keeping prenatal checkups, birth preparations, and childcare arrangements well organized.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/2-4.BE5zxupI_Z1Pq6IP.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;5472&quot; height=&quot;3648&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Meanwhile, I witnessed firsthand how life takes shape bit by bit: starting from a 2mm, blurry little dot—&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/2-1.CTs37CZi_drje1.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1280&quot; height=&quot;806&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Gradually taking human form—&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/2-2.BrfMNF50_1qrbfj.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1280&quot; height=&quot;810&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Until limbs and facial features became clearly visible, moving around in the belly, hiccupping, doing gymnastics. Over those months, I constantly marveled at and felt reverence for the miracle of life. Carbon-based life scores a point back!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/2-3.TEndI9aX_Z19rFO2.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1136&quot; height=&quot;787&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Finally, on the evening of July 27th, Haha (my daughter) was born. Before I could catch my breath, we were told her blood oxygen kept dropping, and she was urgently transferred to the NICU for observation and treatment.&lt;/p&gt;
&lt;p&gt;At that moment, my wife was barely conscious after major surgery on one side, while our newborn Haha’s life hung in the balance on the other. That night was probably the darkest moment of my life. I discovered that all the judgment, problem-solving abilities, and even optimism I usually possessed were completely useless at such a time. The only thing I could do was wait—wait for good news that wasn’t guaranteed to come.&lt;/p&gt;
&lt;p&gt;Fortunately, after several days of treatment, Haha was out of danger. It was a close call, but she was discharged and came home.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1949827364008947806&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1949827364008947806.CmWxYMX8_Zp0yg2.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;1168&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1951235222676316241&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1951235222676316241.CH6pDtvv_14UKlc.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;3648&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1952376061318377652&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1952376061318377652.B2o-dZ1y_Z2sHWz9.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2032&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After the NICU experience, life became smoother. At home, my wife and our helper take care of Haha. She grows every day, learning to grab things, roll over, developing emotions, and recognizing people. She’s also genuinely adorable—every time we take her out for a stroll, passersby come over to compliment her.&lt;/p&gt;
&lt;p&gt;As a new dad, of course I get tired, but more than that, there’s a very grounded sense of happiness. It doesn’t need to be proven or explained—it just becomes very certain the moment you pick her up.&lt;/p&gt;
&lt;h2 id=&quot;making-money&quot;&gt;Making Money&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1874129585069318349&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1874129585069318349.UpTMNRRT_Z1omslW.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;784&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As everyone knows, a child is a four-legged money-devouring beast. Before even leaving the hospital, she had already devoured S$100,000. This very vividly refreshed my understanding of reality.&lt;/p&gt;
&lt;p&gt;As I mentioned, after my wife got pregnant, many things in my mindset changed. The most significant was the change in my attitude toward money.&lt;/p&gt;
&lt;p&gt;Before, of course I wanted to make money, but overall I was content with just enough. Money felt more like a means to pursue hobbies and maintain life, not the ultimate goal, so I was relatively lazy about making money. For example, when choosing jobs before, I would unhesitatingly give up double the salary at Binance for the more hobby-aligned Bilibili. Or four years ago during the Web3 bull market when money was practically lying on the ground, I remained unmoved. This also led to my being quite unsuccessful in personal finance.&lt;/p&gt;
&lt;p&gt;Now this change feels like sliding from indifference straight into overcompensation. I started caring more about security, and I more easily tie security to money. Money’s priority in my life has been significantly elevated. I’ve also become more clearly aware that the confidence behind many choices ultimately comes from cash flow and risk tolerance.&lt;/p&gt;
&lt;p&gt;This year I started investing more actively in cryptocurrency. I entered Web3 four years ago but only started actively buying crypto last year—that in itself is quite remarkable. The process was very bumpy. At one point I lost a lot of money, but I also bought many lessons. Fortunately, the final result was okay, and those lessons are even more valuable than the interim gains.&lt;/p&gt;
&lt;p&gt;This year’s return curve (implementation method: see &lt;a href=&quot;/beancount&quot;&gt;Ancient Artifact Beancount: The Ultimate Double-Entry Bookkeeping Solution for the Crypto &amp;amp; AI Era&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/3-1.BeiWvHUb_2t4KJz.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1978&quot; height=&quot;1002&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;From Gut Feeling to Having a System&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My investment journey can be roughly divided into two phases, with March as the dividing line.&lt;/p&gt;
&lt;p&gt;Before March, I knew almost nothing about strategies or candlestick charts, used no analysis tools, and basically bought and sold based on gut feeling and emotion—like a form of dollar-cost averaging where I’d sell some when it dropped, sell some when it rose. Because the market was generally trending up and volatility wasn’t too wild, even buying blindly produced decent returns, which even made me a bit complacent.&lt;/p&gt;
&lt;p&gt;But the scariest thing about gambling is when it gives you a little sweetness at the beginning.&lt;/p&gt;
&lt;p&gt;Later, during the February tariff war that caused market panic and a major crash, I suffered heavy losses. My mental journey went something like this: During the first leg down, I thought it was an excellent buying opportunity and put almost all my savings into buying the dip. When the market continued to fall, I started to panic but still wanted to hold on, refusing to cut losses. During the third, more violent drop, I completely collapsed and panic-sold all my positions. Result: bought at the high, sold at the absolute low, and the market quickly rebounded.&lt;/p&gt;
&lt;p&gt;Looking back now, one thing I did slightly right was being timid enough not to use leverage—otherwise it would have been worse. But I made a pile of classic mistakes: no strategy, controlled by emotions, large position buying the dip, no position management, chasing rallies and panic selling…&lt;/p&gt;
&lt;p&gt;After March, I started systematically learning analysis methods and tools. I began setting stop-losses and take-profits, controlling positions, controlling emotions, no longer blindly buying dips or panic selling. Gradually I built a more sustainable trading system. I made back what I lost and earned more on top. By the October 11 flash crash, or the end-of-year bull market tail, these drops were even more severe than February, but I didn’t lose significant money again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tools and Strategies&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For trading platforms, I mainly use &lt;a href=&quot;https://www.bjwebptyiou.com/join/1876626&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OKX&lt;/a&gt;—low fees, and using Simple Earn and occasional Flash Trade for some low-risk returns is nice. For market analysis, I mainly use &lt;a href=&quot;https://cn.tradingview.com/pricing/?share_your_love=DIYgod&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;TradingView&lt;/a&gt;—powerful features and rich indicators. For cold wallets, I use &lt;a href=&quot;https://onekey.so/r/N9AHU3&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OneKey&lt;/a&gt;—popular cold wallets all have adequate security, but OneKey’s user experience is leagues ahead for me.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1901239258331169099&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1901239258331169099.77NzdT-p_Z1drLSf.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;976&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As for strategy, I’m now more like constantly adding guardrails for myself rather than chasing some magical holy grail. I remind myself that no matter how confident I am at any moment, the market could go up, could go down, but might also go sideways. I reasonably allocate and frequently review the ratio of cash, coin holdings, and risk investments. I’d rather earn less money than touch things I don’t understand or that haven’t been thoroughly vetted. I have patience, understand that slow is fast, and survival is most important—so now I basically only trade BTC and ETH.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/2000744194747588621&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-2000744194747588621.BsD8yvgJ_Z2eQnzf.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;784&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I also strictly control leverage—mostly 1x, never exceeding 3x. Stop-losses must be clear. I’m willing to admit mistakes and don’t hold losing positions. I try to avoid DeFi because when you don’t know where the yield comes from, you are the yield.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1990353824192843862&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1990353824192843862.Bxl9rheC_Z15eiev.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2144&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For manual trading, I only look at 4-hour or longer timeframes for medium-to-long-term trades. Short-term trading is left to automated strategies to minimize the influence of short-term emotions.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1948375038517727446&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1948375038517727446.CQSpYYGy_2cg23C.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;784&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Some memorable moments:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1972109779322745148&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1972109779322745148.BlhGWAg0_F64Nd.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;880&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1959155822518206531&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1959155822518206531.DOgBbF47_1gJHsz.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;1608&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1993890218555134464&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1993890218555134464.A_S8il_b_212VfM.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2868&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1962515701245337648&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1962515701245337648.CFLim87f_ZGy5WX.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;1852&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1937436310995894571&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1937436310995894571.B_7Tb3fr_Z1izNKn.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2200&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;outdated-open-source-mindset&quot;&gt;Outdated Open Source Mindset&lt;/h2&gt;
&lt;p&gt;Compared to the rapid shift in my attitude toward money, my views on technology and products didn’t turn as smoothly. This year’s main work was on Folo. Folo itself accomplished a lot—new mobile app, local-first architecture, AI, and more. But commercialization implementation and validation progressed too slowly. Combined with some real-world factors, we later had to make some hasty pivots. A lot of technical investment ended up looking like self-entertainment. Worse, there were also frustrating collaboration issues within the team, and even backstabbing from employees after they left, causing chaos. It even more dramatically uncovered other behind-the-scenes bad actors and a string of laughable conspiracy stories—I won’t get into details here.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1995843301757604043&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1995843301757604043.C0LiyX00_QiGuL.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;1168&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Looking back, the delayed commercialization and hasty pivot weren’t coincidental—they were deeply rooted from the start in my and the team’s open-source thinking and engineering-first mindset.&lt;/p&gt;
&lt;p&gt;Open source itself is of course fine. Open source is a great way to pursue hobbies, do side projects, and learn new things. The problem lies in mixing too much open-source thinking into a product that needs a rigorous business loop—it usually doesn’t end well. Because open source defaults to passion-driven and community-driven with negative revenue, slow long-term polishing, while business defaults to capital-driven, rapid trade-offs with limited resources.&lt;/p&gt;
&lt;p&gt;If we concretize this mismatch, it manifests in many ways. For example, the product lacks rigorous preparation, direction is more likely to waver; users include many with low willingness to pay, making later pivots harder; too much weight given to niche feedback, product easily pulled toward niche directions; team recruitment and management too loose, insufficient execution, serious internal friction; wasting enormous time chasing perfection in technical and design details that nobody cares about, paying huge opportunity costs, and so on.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/2003389654951952683&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-2003389654951952683.lueJgu6J_Z1TBBPE.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2048&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For me, this year was like a long-overdue business fundamentals lesson. It doesn’t oppose ideals, nor does it oppose open source, but it forced me to admit that ideals need boundaries, passion needs a cost sheet, and choices must be accountable for their outcomes. Saying all this sounds mundane, but that’s just reality.&lt;/p&gt;
&lt;p&gt;And even setting aside reality, within the open-source ivory tower, for open-source projects driven mainly by personal interest, the author’s passion is often worn down bit by bit during long, lonely maintenance—and by freeloaders and entitled users who feel no shame asking for more.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1932703374283284812&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1932703374283284812.D3Rolmrp_1t7pxK.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2324&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;After experiencing these things, I started to re-examine what open source truly means to me. My attitude toward open source has shifted from absolute advocacy to a complex emotion mixed with wariness and even mild PTSD. Going forward, my investment in open source will probably be more grounded in reality, restrained, and selective.&lt;/p&gt;
&lt;h2 id=&quot;cyber-life&quot;&gt;Cyber Life&lt;/h2&gt;
&lt;p&gt;Let’s lighten up. As per tradition, here’s a summary of this year’s books, shows, games, and apps.&lt;/p&gt;
&lt;h3 id=&quot;apps&quot;&gt;Apps&lt;/h3&gt;
&lt;p&gt;App of the year is of course Folo.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/5-1.CIpIsGRi_Z1R9LPS.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;3840&quot; height=&quot;2400&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;https://diygod.cc/_astro/5-2.DEeG6jwf_ZWEV2x.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2633&quot; height=&quot;1438&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;It consumed most of my time and energy this year and left some painful memories. But if you have stable reading habits and the ability to manage information sources, Folo remains the best information aggregation app in my mind.&lt;/p&gt;
&lt;p&gt;Desktop apps (by the way, AI apps make up a high proportion—6/13):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Notion Calendar: Calendar client, experience is noticeably better than Apple Calendar, UI is cleaner.&lt;/li&gt;
&lt;li&gt;Spark: Email client. Used Spark V2 for a long time. V3 was too bad when it first came out, switched to Mimestream; but Mimestream is too expensive ($50/year), used it for a year then switched back to Spark, found it’s matured a lot now. Though default settings are still anti-human, most can be changed in options.&lt;/li&gt;
&lt;li&gt;TickTick: To-do list.&lt;/li&gt;
&lt;li&gt;&lt;del&gt;ChatGPT Atlas: Browser. Can only be called the temporarily optimal solution; still many problems, especially DevTools are terrible. I feel like I should go back to basics: if Chrome officially supports vertical tabs, I’ll switch back to Chrome.&lt;/del&gt;&lt;/li&gt;
&lt;li&gt;Chrome: Browser. Arc → Dia → ChatGPT Atlas → Chrome. Just after finishing this year-end summary, Atlas self-destructed, so I went back to basics.
&lt;a href=&quot;https://x.com/DIYgod/status/2001635623812370563&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-2001635623812370563.Dwl3MAXr_BChbD.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2428&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Warp: Terminal.&lt;/li&gt;
&lt;li&gt;Folo: Reader I made myself.&lt;/li&gt;
&lt;li&gt;VSCode: Code editor. VSCode → Cursor → after Codex came out, switched back to VSCode.&lt;/li&gt;
&lt;li&gt;Notion: For journaling. Used Notion years ago, then saw Obsidian had a better plugin ecosystem and switched to Obsidian (see &lt;a href=&quot;/obsidian&quot;&gt;Obsidian-based Life Recording System&lt;/a&gt;), but later found none of that matters—writing experience determines whether you want to write and how much you write. Obsidian is limited by Markdown capabilities and can never catch up to Notion. Plus Notion keeps investing more in AI, so I switched back.&lt;/li&gt;
&lt;li&gt;Plex: Media server, watching movies, TV shows, and anime on my NAS.&lt;/li&gt;
&lt;li&gt;NetEase Cloud Music: Logically, being overseas I should use Spotify, but Spotify’s recommendations really don’t suit my taste. NetEase Cloud Music doesn’t provide overseas service due to licensing, so I can only run UnblockNeteaseMusic as a proxy on my NAS—very troublesome.&lt;/li&gt;
&lt;li&gt;Telegram / Discord / Slack / WhatsApp / WeChat: Instant messaging, increasingly fragmented.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://onekey.so/r/N9AHU3&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;OneKey&lt;/a&gt;: Cold wallet, leagues ahead in experience (mentioned in the previous section too).&lt;/li&gt;
&lt;li&gt;Eagle: Image management.&lt;/li&gt;
&lt;li&gt;Googly Eyes: A pair of eyes.
&lt;a href=&quot;https://x.com/DIYgod/status/1899649650841571686&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1899649650841571686.DWSUPBJ7_1SI0ID.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1100&quot; height=&quot;1216&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Services running on my NAS:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AdGuard Home: Ad blocking + faster DNS for home network.&lt;/li&gt;
&lt;li&gt;Fava: Bookkeeping, see &lt;a href=&quot;/beancount&quot;&gt;Ancient Artifact Beancount: The Ultimate Double-Entry Bookkeeping Solution for the Crypto &amp;amp; AI Era&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Gitea: Hosting code that isn’t suitable to be public.&lt;/li&gt;
&lt;li&gt;Home Assistant: Smart home control. Haven’t done much smart home stuff since moving frequently. Mainly used for backup device charging management and scheduled device on/off.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gethomepage.dev&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;homepage&lt;/a&gt;: Too many services, using one portal to manage them centrally.&lt;/li&gt;
&lt;li&gt;Nginx Proxy Manager: Assigning domains to various services for easier access.&lt;/li&gt;
&lt;li&gt;OpenList: Automatically downloading content saved in Aliyun Drive, PikPak, etc. to NAS.&lt;/li&gt;
&lt;li&gt;UnblockNeteaseMusic: Making NetEase Cloud Music work overseas.&lt;/li&gt;
&lt;li&gt;Uptime Kuma: Monitoring service availability.&lt;/li&gt;
&lt;li&gt;ANI-RSS: Automatically downloading subscribed anime for seasonal tracking.&lt;/li&gt;
&lt;li&gt;bili-sync: Downloading Bilibili favorites to NAS.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;electronics&quot;&gt;Electronics&lt;/h3&gt;
&lt;p&gt;Electronic product of the year is OneKey Pro.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/5-4.BG6lJ3tU_2wrnbS.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1600&quot; height=&quot;1076&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Thunderbird V3 was a bit disappointing. I’ve always been interested in Meta’s AI glasses. I thought after all this time, domestic competitors would be more mature, but they’re quite lacking.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1895461087652581702&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1895461087652581702.Dppa4VPt_gTq3d.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2808&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1938871051859173499&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1938871051859173499.BUbD0Ikz_2paMSl.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;3140&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iPhone 13 mini has been used for over four years now, feels like it can last a few more years.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1965676625888235925&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1965676625888235925.DRK062Se_ZGpysK.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2048&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;OPPO Watch X2, on the other hand, I’m quite satisfied with. Got tired of Mi Band, switched to a smartwatch with a bigger screen, very comfortable to use. It has both full smart mode and light smart mode—full smart is complete Android, light smart is like a bigger band with super-long battery life. Health features are also good, can even warn me about high blood pressure risk.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1908104944160555401&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1908104944160555401.BQPaCJAF_1RkB6P.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2036&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1921387552634869763&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1921387552634869763.DzxWKmD-_2thXno.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2048&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Also OneKey Pro and OneKey Classic 1S Pure.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1926533360794394849&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1926533360794394849.rwCrMK10_PQR5g.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2048&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;shows&quot;&gt;Shows&lt;/h3&gt;
&lt;p&gt;Show of the year is &lt;em&gt;Chainsaw Man: Compilation Film&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/5-3.D-g6d8Zj_3F2jw.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;686&quot; height=&quot;386&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;The pacing is much better than the TV version. A lot of the dragging parts were cut, making the overall viewing experience noticeably better. I really wish Demon Slayer would also release a compilation film.&lt;/p&gt;
&lt;p&gt;Most of my time this year went to Folo, so the show count is quite meager.&lt;/p&gt;
&lt;p&gt;Anime:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chainsaw Man: Reze Arc (second favorite, Reze is great)
&lt;a href=&quot;https://x.com/DIYgod/status/1966451474348507570&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1966451474348507570.CxOywCFh_Z29F0pv.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2156&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Chainsaw Man: Compilation Film&lt;/li&gt;
&lt;li&gt;Demon Slayer: Infinity Castle Arc - Chapter 1: Akaza Returns (went to the theater specifically to see it. Maybe my expectations were too high, was a bit disappointed; didn’t feel much about Akaza)
&lt;a href=&quot;https://x.com/DIYgod/status/1956229077934989788&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1956229077934989788.DGZsg1ob_Z26oWMn.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2284&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Takopi’s Original Sin (favorite among new anime)
&lt;a href=&quot;https://x.com/DIYgod/status/1957666854098596130&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1957666854098596130.CLolmQXk_ZrO9RJ.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;1304&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Dusk Maiden of Amnesia&lt;/li&gt;
&lt;li&gt;Spy × Family Season 3&lt;/li&gt;
&lt;li&gt;The Summer Hikaru Died
&lt;a href=&quot;https://x.com/DIYgod/status/1941803538507759698&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1941803538507759698.rihybmdc_1iluG1.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2188&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Ruri’s Gem&lt;/li&gt;
&lt;li&gt;Apocalypse Hotel&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TV Shows:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Walking Dead Seasons 1-11&lt;/li&gt;
&lt;li&gt;Stranger Things Seasons 1-4 (saw Stranger Things themed haunted house at Universal Studios Singapore Halloween event, thought I hadn’t watched it, so I caught up, then discovered I had already watched it three years ago…)&lt;/li&gt;
&lt;li&gt;Black Mirror Season 7&lt;/li&gt;
&lt;li&gt;Love, Death &amp;amp; Robots Season 4&lt;/li&gt;
&lt;li&gt;Shameless Seasons 1-3&lt;/li&gt;
&lt;li&gt;Chang’an’s Lychees&lt;/li&gt;
&lt;li&gt;The Last of Us Season 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Movies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Little Monster from Langrang Mountain&lt;/li&gt;
&lt;li&gt;Ne Zha 2: The Devil Child Stirs Up the Sea&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;books-and-games&quot;&gt;Books and Games&lt;/h3&gt;
&lt;p&gt;Didn’t finish a single book this year, skipping.&lt;/p&gt;
&lt;p&gt;Played &lt;em&gt;It Takes Two&lt;/em&gt; with my wife for a few hours, haven’t finished yet. Also played &lt;em&gt;Honor of Kings&lt;/em&gt; for a few days. Nothing else.&lt;/p&gt;
&lt;h2 id=&quot;other-notable-things&quot;&gt;Other Notable Things&lt;/h2&gt;
&lt;h3 id=&quot;health&quot;&gt;Health&lt;/h3&gt;
&lt;p&gt;Last year’s physical exam revealed very poor liver indicators—continuing like this might lead to major problems. Afterward, my wife mandated I be in bed by 10 PM. My schedule has become much more regular, now roughly sleeping at midnight and waking at 8 AM. But I haven’t had a physical exam this year, so I don’t know if the indicators have improved.&lt;/p&gt;
&lt;p&gt;After Haha was born, we have a helper cooking at home, so diet is healthier. I also quit carbs, using soy milk as my staple, and lost 10 jin (~5 kg), but still 10 jin away from standard weight. Exercise is still almost nonexistent, but daily stroller walks mean I get a few more steps.&lt;/p&gt;
&lt;p&gt;On the other hand, the decline of bodily functions is becoming increasingly obvious. Mainly manifested in decreased energy and known weak points. Plantar fasciitis, cervical spondylosis, knee pain, periodontitis—all these old problems have worsened. Energy is nowhere near what it used to be. This year I started relying on external forces (caffeine, taurine) to maintain work efficiency, but this also led to dependency. When I tried to stop taking taurine, I got severe headaches that could only be relieved with painkillers.&lt;/p&gt;
&lt;h3 id=&quot;living&quot;&gt;Living&lt;/h3&gt;
&lt;p&gt;After Haha joined the family, home became extremely cramped, so we moved to a bigger place, from Singapore’s Queenstown to the more remote Jurong West. Because it’s more remote but more spacious, rent is actually cheaper, and the natural environment is better. There’s also a huge national park called Jurong Lake Gardens nearby, great for stroller walks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/6-1.Bk1Ba0GA_Z1bCCup.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1548&quot; height=&quot;2070&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;travel&quot;&gt;Travel&lt;/h3&gt;
&lt;p&gt;My wife was going stir-crazy and nearly getting depressed during mid-pregnancy, and the middle period is relatively suitable for going out, so we hurried to arrange two relaxing destinations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dali&lt;/strong&gt;’s theme was food. We ate tons of mushrooms. The mushroom soup was incredibly savory, though there’s genuine risk. Before eating, the restaurant even takes a sample—if you get poisoned, you bring the sample to the hospital with you.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/justjustjustcc/status/1907051362036465763&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1907051362036465763.C7XwuCrO_Z6jRT6.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;1868&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1907053924626072055&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1907053924626072055.CA_6eWzp_Z1xSjt1.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2424&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Singapore doesn’t have as much good food.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/justjustjustcc/status/1909977000980385808&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1909977000980385808.Br2f2m8p_ZbcLof.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2096&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/justjustjustcc/status/1911989885998895249&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1911989885998895249.CZLV4L0d_Z1i86fc.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2424&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Erhai Lake has a power that makes you feel at peace. It was at Erhai that my wife and I made our private vows to each other. Returning now, we’re a family of three. Erhai has witnessed our journey from childhood friends, to lovers, to husband and wife, to parents. My love for my wife has grown deeper and more profound with time.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/justjustjustcc/status/1910216881304392131&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1910216881304392131.Ck3N_KOo_vV8jE.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2424&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/justjustjustcc/status/1910689980516839724&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1910689980516839724.D7W-BgPf_Z13ge81.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2128&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1911599563041095989&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1911599563041095989.Btl_G0O3_2s0Jyo.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2144&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bali&lt;/strong&gt; was about completely unwinding. We found a luxury resort, spent every day swimming and sunbathing, letting ourselves briefly forget the daily acceleration.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/justjustjustcc/status/1921916021662765358&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1921916021662765358.BioV8NSl_Z2hXggE.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2016&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/justjustjustcc/status/1922654327950168089&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1922654327950168089.B3a-30qz_2dR0KD.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;3744&quot; alt=&quot;&quot;&gt;&lt;/a&gt;
&lt;a href=&quot;https://x.com/DIYgod/status/1921451501660033114&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1921451501660033114.CXeZTwZh_yDCWo.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;3648&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Also &lt;strong&gt;Halloween Horror Nights at Universal Studios Singapore&lt;/strong&gt;. This year there were four themed haunted houses: Stranger Things, Singapore’s Most Haunted, The Way of the Weird Immortal, and Thailand’s Death Whisperers. Incredibly scary, screaming the whole time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/6-2.C0f7YmiQ_Z1UP7Qa.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2531&quot; height=&quot;2160&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;interesting-little-things&quot;&gt;Interesting Little Things&lt;/h3&gt;
&lt;p&gt;A hard drive in my NAS failed. It automatically went into read-only mode, but no data was lost.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1981460911304167588&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1981460911304167588.CGXHRgZU_Z1M3WBm.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2316&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Went around collecting birthday freebies.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1932084384540102892&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1932084384540102892.DfmHfuL__1PmLSY.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;3744&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Watched my wife build Legos.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://x.com/DIYgod/status/1935675896821899268&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/x-1935675896821899268.1pvxVP_t_25FKPg.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2268&quot; height=&quot;2424&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;2026-wishes&quot;&gt;2026 Wishes&lt;/h2&gt;
&lt;p&gt;My wishes for 2026 are quite simple. I hope my body won’t remind me with more bad news. I hope I can keep making money. I hope I can get my Singapore status sooner, reducing some uncertainty about the future.&lt;/p&gt;
&lt;style&gt;
  .prose :where(img):not(:where([class~=&quot;not-prose&quot;],[class~=&quot;not-prose&quot;] *)) {
    border: 1px solid #eee;
  }
&lt;/style&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded><category>Diary</category><category>Year in Review</category></item><item><title>ZURICH INTERLAKEN, PARIS</title><link>https://diygod.cc/en/europe-travel</link><guid>europe-travel</guid><pubDate>Sat, 20 Dec 2025 14:30:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;December 2022&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded><category>Creations</category><category>vlog</category></item><item><title>Ancient artifact Beancount: the ultimate double-entry accounting solution in the era of Crypto and AI</title><link>https://diygod.cc/en/beancount</link><guid>beancount</guid><description>Many times we find bookkeeping troublesome, not because the UI is not beautiful enough and the interaction is not smooth enough, but because the bookkeeping method is not scientific enough. When the method is scientific enough, you don’t even need a UI. A CLI + a few plain text files + a set of clear syntax can describe the financial world very accurately.</description><pubDate>Fri, 12 Dec 2025 08:58:00 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 id=&quot;accounting&quot;&gt;Accounting&lt;/h2&gt;
&lt;p&gt;Accounting is essentially answering three questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Where does the money come from?&lt;/li&gt;
&lt;li&gt;Where is the money now?&lt;/li&gt;
&lt;li&gt;Where did the money end up?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It sounds simple, but as long as your financial activities are a little more complicated, these three questions will immediately become a “multivariable equation”:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple accounts: bank cards, Alipay, credit cards, exchanges, on-chain wallets…&lt;/li&gt;
&lt;li&gt;Multiple asset types: savings, loans, investments…&lt;/li&gt;
&lt;li&gt;Multiple currencies: CNY, USD, BTC, various altcoins…&lt;/li&gt;
&lt;li&gt;Multiple time dimensions: installment, advance, loan…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Because of this, I have changed several plans.&lt;/p&gt;
&lt;p&gt;2016–2021: NetEase is rich - the needs at that time were very simple: recording RMB income, savings, and daily expenses. It can automatically synchronize bank statements and Alipay and WeChat bills, which is very convenient for domestic scenarios.&lt;/p&gt;
&lt;p&gt;2021–2024: MoneyWiz - I need multiple currencies after moving abroad and mainly use a UK bank account. MoneyWiz is very convenient for synchronizing multiple currencies and overseas accounts.&lt;/p&gt;
&lt;p&gt;2024: I started recording crypto assets. At this time, the situation became very complicated. I not only wanted to know “how much the total amount has increased”, but also wanted: the details of each purchase/sale, the proportion of different assets, the daily profit and loss, and a more rigorous calculation of the rate of return.&lt;/p&gt;
&lt;p&gt;MoneyWiz has very limited support for investment/encrypted assets; I tried Youzhiyouxing again. It can record the total amount before and after the investment and calculate the rate of return, but it cannot record the details of each transaction. Many prices need to be maintained manually, and floating profits and losses cannot be automatically calculated.&lt;/p&gt;
&lt;p&gt;Finally I came across double-entry accounting and Beancount, which completely solved my problem.&lt;/p&gt;
&lt;p&gt;I realized that many times we find bookkeeping troublesome not because the UI is not beautiful enough and the interaction is not smooth enough, but because the bookkeeping method is not scientific enough. When the method is scientific enough, you don’t even need a UI. A CLI + a few plain text files + a set of clear syntax can describe the financial world very accurately.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;double-entry-accounting&quot;&gt;Double-entry accounting&lt;/h2&gt;
&lt;p&gt;Double-entry accounting may seem counterintuitive at first, but once you master it, you’ll find it to be incredibly powerful and flexible, and it will force you to understand your financial activities in a clearer way.&lt;/p&gt;
&lt;h3 id=&quot;five-buckets-account-type&quot;&gt;Five buckets (account type)&lt;/h3&gt;
&lt;p&gt;Double-entry accounting will classify all accounts into five categories (can be thought of as five buckets of beans):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Assets&lt;/strong&gt;: cash, bank deposits, securities, on-chain assets…&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Liabilities&lt;/strong&gt;: credit card debt, mortgage, car loan…&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Income&lt;/strong&gt;: salary, bonus, interest…&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expenses&lt;/strong&gt;: Eat, shop, travel, subscribe…&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Equity&lt;/strong&gt;: “base” for adjustment/archiving, such as opening balance, error correction, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;all-financial-activities-are-transfers-between-buckets&quot;&gt;All financial activities are “transfers between buckets”&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Income → Assets&lt;/strong&gt;: Pay wages (the income bucket decreases and the asset bucket increases)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asset → Spend&lt;/strong&gt;: Buy something (the asset bucket decreases and the spending bucket increases)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Liability → Asset&lt;/strong&gt;: Borrow money (the liability bucket increases, the asset bucket increases)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Asset → Liability&lt;/strong&gt;: Repayment (Asset bucket decreases, Liability bucket decreases)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spend → Asset&lt;/strong&gt;: Return (the spending bucket decreases and the asset bucket increases)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assets → Income&lt;/strong&gt;: Investment losses (assets bucket decreases, income bucket decreases)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You will find that according to this sign convention, the balance of the income account is often negative. This is not a bug, but a different accounting perspective.&lt;/p&gt;
&lt;p&gt;Think of it this way: Think of “income” as a bucket containing the fruits of your life (past and future). Every time you get a salary, you are actually taking some of it out of this bucket and putting it into your assets. The more you take, the more negative the value in the income bucket becomes.&lt;/p&gt;
&lt;p&gt;So negative income doesn’t mean “you owe income,” it’s just that you’re taking assets out of the bucket. This also involves a core rule of double-entry accounting: in a transaction, the sum of all numbers must equal 0. This is also the key to verifying its correctness: if you miss one stroke or write one wrong digit, the system will alarm you on the spot.&lt;/p&gt;
&lt;p&gt;Under the common notation conventions of Beancount, you will see “accounting identity” written like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;(收入 + 负债) + (资产 + 花费) = 0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also think of it in a more traditional way: whatever you earn or borrow ultimately either becomes an asset you own or is spent.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;beancount&quot;&gt;Beancount&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/beancount/beancount&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Beancount&lt;/a&gt; is the most comfortable plain text double-entry accounting tool I have ever used and the most suitable for the complex financial world.&lt;/p&gt;
&lt;p&gt;It is not the pioneer of double-entry accounting software. The earlier representative is &lt;a href=&quot;https://github.com/ledger/ledger&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Ledger&lt;/a&gt;. But Beancount has made a lot of improvements on the Ledger concept and has stood the test of time (started in 2007).&lt;/p&gt;
&lt;p&gt;The most direct way to learn Beancount is to read the official documentation: &lt;a href=&quot;https://docs.google.com/document/d/1P5At-z1sP8rgwYLHso5sEy3u4rMnIUDDgob9Y_BYuWE&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Getting Started with Beancount&lt;/a&gt;, &lt;a href=&quot;https://docs.google.com/document/d/1wAMVrKIA2qtRGmoVDSUBJGmYZSygUaR0uOMW1GV3YE0&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Beancount Language Syntax&lt;/a&gt;, or this article is also good: &lt;a href=&quot;https://wzyboy.im/post/1063.html&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Beancount - Command line double-entry bookkeeping | wzyboy’s blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this blog, I will not go into the details of syntax and usage, but will use a few examples to let you quickly feel the power of Beancount and why it is more suitable for the era of Crypto and AI.&lt;/p&gt;
&lt;h3 id=&quot;handle-complex-transactions&quot;&gt;Handle complex transactions&lt;/h3&gt;
&lt;p&gt;Beancount can be used to handle complex transactions gracefully. The following examples involve many-to-many, multi-account + multi-person complex transactions, but they can be described clearly.&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2016&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;02&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;05&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;饭店&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;和室友吃饭&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Cash:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Wallet&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                         -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;300.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; CNY&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; ; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;我用现金支付&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Receivables:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Bob&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                     -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;200.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; CNY&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; ; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;室友帮我付的现金&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:Food:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;DiningOut&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                    +&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;250.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; CNY&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; ; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;AA&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 我的一半&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Receivables:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Bob&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                     +&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;250.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; CNY&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; ; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;AA&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 室友的一半&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s what happened at the same time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You paid part of it in cash&lt;/li&gt;
&lt;li&gt;The roommate also paid part of the money in cash.&lt;/li&gt;
&lt;li&gt;AA share&lt;/li&gt;
&lt;li&gt;You incur/offset receivables against roommates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, you only need to look at the balance of &lt;code&gt;Assets:Receivables:Bob&lt;/code&gt; to know how much you owe your roommate, and the balance of &lt;code&gt;Expenses:Food:DiningOut&lt;/code&gt; to know how much you spend on food every month.&lt;/p&gt;
&lt;p&gt;If you use “single-entry accounting + remarks” to pile up, you will basically be unable to calculate later. However, double-entry accounting can directly write out the structure, and then statistics will become very natural.&lt;/p&gt;
&lt;h3 id=&quot;processing-multi-currency-transactions&quot;&gt;Processing multi-currency transactions&lt;/h3&gt;
&lt;p&gt;Cross-currency transactions often involve exchange rate issues, and Beancount can also handle them gracefully.&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2016&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;02&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;商店&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;买东西&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Assets:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Cash&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                                 -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;200.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Liabilities:CMB:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;CreditCards&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                 -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;650.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; CNY&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; @@ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;100.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Expenses:Clothing:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Pants&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                     +&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;150.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Expenses:Clothing:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Shoes&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                     +&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;150.00&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This kind of scenario where “the credit card is denominated in RMB, but the actual consumption is denominated in US dollars” is very common in cross-border life. The expression of Beancount is very straightforward: use a record to bind the relationship between the currencies on both sides and the current exchange rate, and subsequent queries/reports can continue to be derived based on it.&lt;/p&gt;
&lt;h3 id=&quot;more-suitable-for-crypto-transactions&quot;&gt;More suitable for Crypto transactions&lt;/h3&gt;
&lt;p&gt;Beancount does not limit currency units: USD, CNY, BTC, USDT, or even any custom token you can use. Coupled with the SQL-like query tool &lt;code&gt;bean-query&lt;/code&gt;, it is very convenient to use for crypto asset investment analysis.&lt;/p&gt;
&lt;p&gt;Let’s take an example that looks complicated but is very concisely recorded:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;07&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;23&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; price&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;07&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;买BTC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USD&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;            -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;150&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;            149&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:TradingFee:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Crypto&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;      1&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;07&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;买BTC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USD&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;             -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;60&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;             19&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;3&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:TradingFee:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Crypto&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;      3&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;07&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;卖BTC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;           -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;198&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  {} @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USD&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;             298&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Income:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Investment&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;07&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;25&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; price&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt; 4&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This example includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multiple purchases with different costs&lt;/li&gt;
&lt;li&gt;handling fee&lt;/li&gt;
&lt;li&gt;Automatically match costs when selling (&lt;code&gt;{}&lt;/code&gt; let Beancount find it yourself)&lt;/li&gt;
&lt;li&gt;Market price (&lt;code&gt;price&lt;/code&gt;) changes over time&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Based only on these records, Beancount can automatically calculate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Total revenue/realized revenue/unrealized revenue&lt;/li&gt;
&lt;li&gt;Various types of rates of return (such as capital-weighted rate of return Modified Dietz, time-weighted rate of return TWRR, or even any income calculation you customize)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words: you are only responsible for writing the facts clearly, and any complex calculations can be handed over to Beancount.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;my-beancount-practice&quot;&gt;My Beancount practice&lt;/h2&gt;
&lt;p&gt;Treat the ledger as code management.&lt;/p&gt;
&lt;h3 id=&quot;editor&quot;&gt;Editor&lt;/h3&gt;
&lt;p&gt;I use VSCode, with &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=Lencerf.beancount&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Beancount&lt;/a&gt;, &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=dongfg.vscode-beancount-formatter&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Beancount Formatter&lt;/a&gt; and the following configurations to achieve syntax highlighting, auto-completion, error checking, formatting, etc.&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;json&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  &quot;beancount.mainBeanFile&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;main.bean&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  &quot;beancount.runFavaOnActivate&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  &quot;beancount.completePayeeNarration&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  &quot;beancount.fixedCJKWidth&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  &quot;beancount.inputMethods&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: [&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;pinyin&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  &quot;beancount.instantAlignment&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  &quot;editor.formatOnSave&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/1.BV4P0ASo_T0cy9.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1740&quot; height=&quot;388&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;project-structure&quot;&gt;Project structure&lt;/h3&gt;
&lt;p&gt;Beancount naturally supports modularity: different types of transactions are split into different files and then &lt;code&gt;include&lt;/code&gt; enter the main entrance, so the maintenance cost is very low.&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;books&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   ├── &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   │   ├── 2025.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   │   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;cn&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   │   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;crypto&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   │   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;daily&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   │   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;periodic&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   │   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;prices&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   └── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;books&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;scripts&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;importer&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;dbs&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;credit&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;importer&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;dbs&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;debit&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;│   ├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;importer&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;okx&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;js&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;accounts&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;commodity&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;config&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;pbtxt&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;dashboards&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;yaml&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;├── &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;main&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;main.bean&lt;/code&gt; is the entry file, which contains configuration, plug-ins and include:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;option&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;title&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;DIYgod&apos;s Book&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;option&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;operating_currency&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;USD&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;option&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;booking_method&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;LIFO&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;option&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;account_rounding&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;Equity:Rounding-Error&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;plugin&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;beancount_periodic.recur&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;plugin&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;beancount_periodic.split&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; custom&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;fava-option&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;auto-reload&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; custom&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;fava-option&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;default_page&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;income_statement/?conversion=units&amp;amp;interval=day&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; custom&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;fava-extension&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;fava_portfolio_returns&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;  &apos;beangrow_config&apos;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&apos;config.pbtxt&apos;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;2024-01-01 custom &quot;&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;fava&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;extension&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;fava_dashboards&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;  &apos;config&apos;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&apos;dashboards.yaml&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;include &quot;&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;accounts&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;include &quot;&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;books&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;/&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;books&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#96E072&quot;&gt;include &quot;&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;commodity&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;bean&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;accounts.bean&lt;/code&gt; Define all accounts (omitted):&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Expenses&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 费用&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; —— &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;外出就餐&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;、&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;购物&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;、&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;旅行等&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1995&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;06&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;09&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Expenses:Food:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Restaurant&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 省略若干行&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Income&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 收入&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; —— &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;工资&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;、&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;奖金等&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2020&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;04&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Income:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Salary&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 省略若干行&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Assets&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 资产&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; —— &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;现金&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;、&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;银行存款&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;、&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;有价证券等&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Assets:Bank:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;DBS&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; SGD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 省略若干行&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Liabilities&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 负债&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; —— &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;信用卡&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;、&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;房贷&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;、&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;车贷等&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Liabilities:Bank:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;DBS&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; SGD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 省略若干行&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Equity&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 权益&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; —— &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;用于&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;「&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;存放&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;」&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;某个时间段开始前已有的豆子&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Opening&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Balances&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2024&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; open&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Rounding&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Error&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;commodity.bean&lt;/code&gt; Define the currency/asset and use &lt;code&gt;price&lt;/code&gt; to specify the price source (automatic updates will be discussed later):&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; commodity&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  name: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;Tether&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; commodity&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  name: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;Bitcoin&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  price: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;USD:coinbase/BTC-USD&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; commodity&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  name: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;United States Dollar&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; commodity&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; QQQ&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  name: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;NASDAQ-100 Index&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  price: &lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt;&quot;USD:alphavantage/price:QQQ:USD&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;books/2025/prices.bean&lt;/code&gt; is used to record daily prices (automatically generated) so that the profit and loss of positions can be calculated:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; price&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt; 93346.48&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;02&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; price&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt; 94384.76&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; 省略若干行&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;several-common-crypto-transactions&quot;&gt;Several common Crypto transactions&lt;/h3&gt;
&lt;p&gt;The following examples come from my actual practice: transactions are not filled in by hand, but automatically generated from OKX transaction records via &lt;code&gt;scripts/importer-okx.js&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;stablecoin-spot-trading&quot;&gt;Stablecoin Spot Trading&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Buy: Use USDT to buy BTC + handling fee&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;14&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;Buy BTC with USDT&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USDT&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                       -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;3022.493&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                     0.0302113288&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;100000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:TradingFee:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;OKX&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I leave the handling fee blank and let Beancount automatically fill it in (using the “each transaction adds up to 0” rule).&lt;/p&gt;
&lt;p&gt;But if your BTC may come from different stablecoins such as USDT and USDC, the subsequent statistical income will involve exchange rate and currency conversion. In order to make the analysis more unified, I often use a transition account to unify the costs into USD:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;14&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;Buy BTC with USDT&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USDT&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                       -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;3022.493&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                               3022.493&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                              -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;3022.493&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                     0.0302113288&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;100000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:TradingFee:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;OKX&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Equity:Exchange&lt;/code&gt; functions like a “unified conversion layer”: it converts different stablecoin transactions into USD, and subsequent reports will be very comfortable.&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Sell: Sell BTC to get USDC + automatic profit calculation&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;09&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;18&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;Sell BTC for USDC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                        -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;0.100011&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {} @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;117600&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                         11756.00101788&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                        -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;11756.00101788&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USDC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                  11756.00101788&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:TradingFee:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;OKX&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;             -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;5.29258212&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Income:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Investment&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;{} @ 117600 USD&lt;/code&gt;: The cost price is automatically matched from historical records by Beancount, and the selling price is 117600 USD&lt;/li&gt;
&lt;li&gt;The extra part goes into &lt;code&gt;Income:Investment&lt;/code&gt;: the income comes naturally in this way&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you don’t need unified conversion, it can also be simplified to:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;09&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;18&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;Sell BTC for USDC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                        -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;0.100011&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {} @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;117600&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USDC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                  11756.00101788&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:TradingFee:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;OKX&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;             -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;5.29258212&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDC&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Income:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Investment&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;coin-spot-trading&quot;&gt;Coin spot trading&lt;/h4&gt;
&lt;p&gt;Buy ETH with BTC&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;08&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;22&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;Buy ETH with BTC&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;            -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;0.025828016279999998&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {} @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;112480.295&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                       2&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;905.1428904392&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                      -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;,&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;905.1428904392&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;ETH&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                     0.6820274505&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; ETH&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;4224.16&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Income:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Investment&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Expenses:TradingFee:Crypto:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;OKX&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;            0.0001705495&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; ETH&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;4224.16&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The cost/benefit of cryptocurrency transactions is more likely to be confusing, but after unifying the process to the USD caliber, it will become very clear: ETH exchanged with BTC has a clear USD cost; handling fees and subsequent earnings can also be correctly included.&lt;/p&gt;
&lt;h4 id=&quot;u-based-contract&quot;&gt;U-based contract&lt;/h4&gt;
&lt;p&gt;The U-based contract does not actually sell BTC within the exchange, but in order to reflect the profit and loss on asset changes, I use a “borrowed currency sell/buy back” approximate model to express:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Opening a short position: equivalent to “borrow BTC, sell, and exchange for USDT”&lt;/li&gt;
&lt;li&gt;Close position: Equivalent to “use USDT to buy back BTC and return it”&lt;/li&gt;
&lt;li&gt;Profit and loss will naturally be reflected in the final extra (or missing) part&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;01&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;合约 BTC-USDT 做空开仓&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:Futures:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                      -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;100000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                                1000000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                               -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1000000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:Futures:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USDT&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                 1000000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;12&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;合约 BTC-USDT 做空平仓&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:Futures:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;USDT&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                 1000000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                                1000000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Equity:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Exchange&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                               -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1000000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USDT&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; @ &lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:Futures:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;                      -&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;80000&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Income:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Investment&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Real contracts will also involve margin, funding fees, forced liquidation, settlement currency differences, etc. A more rigorous approach can continue to split the account model, but this writing method can already record and calculate the results clearly in most scenarios.&lt;/p&gt;
&lt;h4 id=&quot;coin-margined-contract&quot;&gt;Coin-margined contract&lt;/h4&gt;
&lt;p&gt;Coin-margined contracts are more complex: the position is typically “USD-denominated BTC price exposure” rather than a fixed amount of BTC. It will be very complicated to calculate floating profits and losses based on daily prices, which requires a “double-account model” (factual account + valuation account).&lt;/p&gt;
&lt;p&gt;I currently choose to simplify it to this: manually calculate the profit in BTC when closing the position, and only record the final BTC change, but this will sacrifice the data of the unrealized profit of the current position:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;2025&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;11&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;21&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;OKX&quot;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;合约 BTC-USD 1.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Income:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Investment&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  Assets:Crypto:OKX:&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;BTC&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;                0.007433571994392&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; BTC&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;span style=&quot;color:#F39C12&quot;&gt;85400&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; USD&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;automation-script--ai-accounting&quot;&gt;Automation script + AI accounting&lt;/h3&gt;
&lt;p&gt;Through automated scripts and AI, the accounting workload can be greatly simplified, turning “accounting” into “reconciliation.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automatically generate transaction records&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Beancount is plain text, which means it is naturally suited to automating record generation and updating asset prices.&lt;/p&gt;
&lt;p&gt;There are many import tools in the community, such as: &lt;a href=&quot;https://github.com/beancount/beangulp&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;beangulp&lt;/a&gt;, &lt;a href=&quot;https://github.com/beancount/smart_importer&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;smart_importer&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But the ones I use most are self-written scripts (such as OKX’s &lt;code&gt;scripts/importer-okx.js&lt;/code&gt;, DBS’s &lt;code&gt;scripts/importer-dbs.js&lt;/code&gt;). The principles are similar: obtain transaction data from the interface or web page → convert it into Beancount text.&lt;/p&gt;
&lt;p&gt;For some accounts that do not have interfaces or even export bills (such as the Trust I am using now), it is also very hassle-free to directly take screenshots and send them to AI for recognition:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/2.-3efwuZZ_ZMxTBM.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2418&quot; height=&quot;1842&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automatically update prices&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Beancount itself is an offline system and will not automatically pull prices from the Internet, so I will write the latest currency price/stock price into &lt;code&gt;books/2025/prices.bean&lt;/code&gt; every day, so that the report can calculate the current day’s valuation and unrealized profits.&lt;/p&gt;
&lt;p&gt;The official provides &lt;a href=&quot;https://github.com/beancount/beanprice&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;beanprice&lt;/a&gt; out of the box, which supports Coinbase, Coinmarketcap, Alphavantage and other sources. It can be updated with one command:&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;sh&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt;bean-price&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; -i&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; --update-rate&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; daily&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; --no-cache&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; main.bean&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; &amp;gt;&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; books/2025/prices.bean&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;ai-analysis-and-visualization&quot;&gt;AI Analysis and Visualization&lt;/h3&gt;
&lt;p&gt;Many people stop at “writing down” their accounting and end it, but what is truly valuable and easily forgotten is how to analyze it after recording.&lt;/p&gt;
&lt;p&gt;Beancount is very exaggerated in its analysis and visualization capabilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Web interface that can install plug-ins: &lt;a href=&quot;https://github.com/beancount/fava&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Fava&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;SQL-like query language: BQL (Beancount Query Language)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s take a look at some of the effects I commonly use:&lt;/p&gt;
&lt;p&gt;Price chart of investment products (included with Fava):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/fava-1.C8Cigb3S_1D95z3.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2956&quot; height=&quot;2006&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Yield curves of different investment products, as well as various weighted returns (plug-in &lt;a href=&quot;https://github.com/andreasgerstmayr/fava-portfolio-returns&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Fava Portfolio Returns&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/fava-2.B-Mn62Al_Z2bVnzI.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2956&quot; height=&quot;2006&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;https://diygod.cc/_astro/fava-5.C7boDPm9_5AE08.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2956&quot; height=&quot;2006&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Change curve, distribution proportion, list of specific assets (plug-in &lt;a href=&quot;https://github.com/andreasgerstmayr/fava-dashboards&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Fava Dashboards&lt;/a&gt; + custom BQL):&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/fava-3.C9cmBQgI_Z1WLbCu.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2956&quot; height=&quot;1667&quot; alt=&quot;&quot;&gt;
&lt;img src=&quot;https://diygod.cc/_astro/fava-4.Wt8zB8Wi_ZNtaMq.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;2956&quot; height=&quot;2006&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;What’s even better is: now with vibe coding, times have changed.
Now you don’t even need to learn how to write BQL first – you can just let AI write it.&lt;/p&gt;
&lt;p&gt;For example, I would make a request like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Using the Fava Dashboards plug-in, write BQL in dashboards.yaml to implement a graph showing the changes in all my cryptocurrency positions over time.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Personalized financial analysis that once took a long time can now be just one sentence.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I have been using Beancount for a year, and almost all my needs have been met. I am very satisfied and have shared it within the company. This blog is to organize the shared content into text, hoping to recommend it to more people.&lt;/p&gt;
&lt;p&gt;I also admit that the threshold for Beancount is not low for most people - it is more like a “programming language for writing ledgers” rather than a little app.
But I also believe that with the development of AI, these barriers will become lower and lower. In the future, everyone can control this powerful tool at a lower cost and achieve full control of their own finances.&lt;/p&gt;
&lt;p&gt;If you are also experiencing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Multi-currency life&lt;/li&gt;
&lt;li&gt;More and more investment products&lt;/li&gt;
&lt;li&gt;The more the accounts are kept, the more confusing they become.
-Analyses are becoming increasingly complex&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then I suggest you spend at least half a day trying Beancount.
It may make you feel for the first time: It turns out that I can finally explain money clearly.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded><category>Sharing</category></item><item><title>The collapse and rebirth of a six-year-old open source project</title><link>https://diygod.cc/en/6-year-of-rsshub</link><guid>10-2532</guid><description>I have an open source project that I have maintained for six years, RSSHub, and it is facing collapse

On the surface, it has close to 30k Stars, more than 900 Contributors, more than 300 million requests per month and countless users, dozens of sponsorships per month, a steady stream of issues and PRs, and the code is updated almost every day, making it very healthy and full of vitality.</description><pubDate>Sun, 10 Mar 2024 19:29:05 GMT</pubDate><content:encoded>&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;I have an open source project that I have maintained for six years, RSSHub, and it is facing collapse&lt;/p&gt;
&lt;h2 id=&quot;background&quot;&gt;Background&lt;/h2&gt;
&lt;p&gt;On the surface, it has nearly 30k Stars, more than 900 Contributors, more than 300 million requests per month and countless users, dozens of dollars of monthly sponsorships, a steady stream of issues and PRs, and the code is updated almost every day. It is very healthy and dynamic, but invisibly, the high maintenance time cost that lasts for several years, the server cost of more than a thousand dollars per month, and the tedious and gradually accumulating maintenance work repeated every day, make it repeatedly jump on the edge of collapse.&lt;/p&gt;
&lt;p&gt;The project was developed six years ago. Many of the fashionable Node.js technology stacks and dependency libraries with the slogan “Next Generation” at that time have become tears of the times. They are very old now and many popular new technologies cannot be applied, such as JSX, TypeScript, Serverless, etc.; its architecture is also very unreasonable. The information of each route is scattered in multiple places. Developing or changing a route requires multiple modifications. There is one place to register the route, one place to write the routing script, and one place to write Radar. Rules, one place to write documentation… This adds a lot of work and is prone to errors. It was not a problem when there were few routes, but now it has become unbearable&lt;/p&gt;
&lt;p&gt;Maintaining the status quo under such a poor infrastructure is already a struggle. Developing new features is even more difficult. It will only increase the difficulty of future updates, so it is difficult to realize the novel ideas that sometimes pop up in my mind.&lt;/p&gt;
&lt;p&gt;The only way to solve these problems is to rewrite the kernel using a modern framework and a newly designed architecture. However, as there are more and more routes, the cost of transformation is getting higher and higher. Each basic change may require up to several months of work. Therefore, although the problem is becoming more and more serious, we continue to postpone it based on the principle that it is not unusable.&lt;/p&gt;
&lt;p&gt;But it had to be done, so I took the time to spend a few months redesigning and rewriting it&lt;/p&gt;
&lt;h2 id=&quot;technology-stack-update&quot;&gt;Technology stack update&lt;/h2&gt;
&lt;h3 id=&quot;koa---hono&quot;&gt;koa -&amp;gt; Hono&lt;/h3&gt;
&lt;p&gt;The first step, which is also the most basic and difficult, is to replace the previously used Web framework &lt;a href=&quot;https://github.com/koajs/koa&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;koa&lt;/a&gt;. As a next-generation web framework that was popular six years ago, the author has long since abandoned it. After research, he decided to switch to &lt;a href=&quot;https://github.com/honojs/hono&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Hono&lt;/a&gt;, which has the best support for JSX, TypeScript, and Serverless.&lt;/p&gt;
&lt;p&gt;Their APIs are very different and require rewriting all middleware and replacing the koa API used in all routes&lt;/p&gt;
&lt;p&gt;Major changes:
&lt;a href=&quot;https://github.com/DIYgod/RSSHub/pull/14295&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/pull/14295&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/QmUBrZkbHVDyfjoMK661QkLiYLnSbBke6quM5SCs2pnFsG.DYJQthnc_Z2CEN9.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;682&quot; height=&quot;90&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;Hono author also likes this transformation&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/yusukebe/status/1762801106340782222&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://twitter.com/yusukebe/status/1762801106340782222&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;javascript---typescript&quot;&gt;JavaScript -&amp;gt; TypeScript&lt;/h3&gt;
&lt;p&gt;Switching to TypeScript can avoid many type problems and low-level errors. The most important thing is to ensure that hundreds of contributors are consistent and difficult to make mistakes, and the quality of routing code for subsequent contributions is not too bad.&lt;/p&gt;
&lt;p&gt;Major changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/pull/14295&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/pull/14295&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/pull/14632&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/pull/14632&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/QmePQXUgzwrKKy8bwRNRARMoXYfPe2ShvqYffYRXhWkbtf.D3X4HfvU_Un8Et.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;728&quot; height=&quot;84&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/DIYgod/status/1764360942035312879&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://twitter.com/DIYgod/status/1764360942035312879&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;commonjs---esm&quot;&gt;CommonJS -&amp;gt; ESM&lt;/h3&gt;
&lt;p&gt;ESM is a specification strongly promoted by some Node.js core developers a few years ago. It has some advantages, but most of them are criticized for ecological fragmentation and functional simplification caused by incompatibility with the previous CommonJS.&lt;/p&gt;
&lt;p&gt;After several years of development, it can now be said that most scenarios are barely usable. &lt;a href=&quot;https://github.com/privatenumber/tsx&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;tsx&lt;/a&gt; also provides support for scenarios where CommonJS and ESM are mixed.&lt;/p&gt;
&lt;p&gt;Although we have tried our best, there are still some CommonJS codes that are temporarily difficult to migrate. As a result, they can only be run using tsx and are incompatible with some Serverless such as Vercel. However, there is still an opportunity to solve it slowly in the future.&lt;/p&gt;
&lt;p&gt;Major changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/pull/14619&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/pull/14619&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/pull/14691&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/pull/14691&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/pull/14632&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/pull/14632&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/QmReUSKAH6qZpavbpotTxsdQ3DwH8A6ughVaBnEpk4k3Xy.DGp1QP_A_ZUVO4p.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;662&quot; height=&quot;80&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/QmTd5sWm6kzZunsv7sQH3djekip5XY44B4LEx3z9XAuvz1.CjR1ixcA_Z1dz6eo.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;680&quot; height=&quot;82&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;art-template---jsx&quot;&gt;art-template -&amp;gt; JSX&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/aui/art-template&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;art-template&lt;/a&gt; is a template engine that supports koa. I remember there was a more popular template engine six years ago, but I can’t remember the name. I chose art-template because I didn’t understand the more popular one at the time. It was very simple.&lt;/p&gt;
&lt;p&gt;Hono comes with JSX support. There is no need to introduce JSX. The syntax extension of JavaScript is equivalent to using React.&lt;/p&gt;
&lt;p&gt;Major changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/commit/3bfdf9427cb8cf063cf7d231ec621278495f5a44&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/commit/3bfdf9427cb8cf063cf7d231ec621278495f5a44&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/commit/94cf0742afa8bf18510ad9ded9b76dcd2ad52c90&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/commit/94cf0742afa8bf18510ad9ded9b76dcd2ad52c90&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;jest---vitest&quot;&gt;Jest -&amp;gt; Vitest&lt;/h3&gt;
&lt;p&gt;Jest was once a popular testing framework, but it has become less and less popular after the advent of the ESM era. The support for ESM has always been “experimental support”, and now Vitest is more popular.&lt;/p&gt;
&lt;p&gt;Major changes:
&lt;a href=&quot;https://github.com/DIYgod/RSSHub/commit/38e42156a0622a2cd09f328d2d60623813b8df28&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/commit/38e42156a0622a2cd09f328d2d60623813b8df28&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;got--&quot;&gt;Got -&amp;gt; ?&lt;/h3&gt;
&lt;p&gt;The currently used &lt;a href=&quot;https://github.com/sindresorhus/got&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;Got&lt;/a&gt; is no longer actively maintained, and no good replacement has been found. It may be replaced with native Fetch or self-encapsulated Fetch in the future, but I haven’t started yet.&lt;/p&gt;
&lt;h2 id=&quot;new-routing-standard&quot;&gt;New routing standard&lt;/h2&gt;
&lt;p&gt;My own abilities are still not enough. I have learned and improved a lot during discussions with community developers. The process is very interesting: &lt;a href=&quot;https://github.com/DIYgod/RSSHub/issues/14685&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/issues/14685&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Major changes:
&lt;a href=&quot;https://github.com/DIYgod/RSSHub/pull/14718&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/pull/14718&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://diygod.cc/_astro/QmPSFiYa8fKZZuFNcptWSpfGwRFTt1S2nfy9CGHFFUKsqe.Bkb6SX5W_Z1Da2mO.webp&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;698&quot; height=&quot;80&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;history&quot;&gt;History&lt;/h3&gt;
&lt;p&gt;The new standard is mainly to solve the problem of too scattered routing information. This should be considered the third edition.&lt;/p&gt;
&lt;p&gt;The first version came from the development stage of RSSHub. At that time, it was not foreseen that there would be so many routes, so there was almost no planning. All routes were registered in the same file, and then routing scripts and documents were added. Later, the file became larger and larger, and it was easy to conflict. In addition, all routing scripts were loaded during the startup phase, and the program performance became worse and worse.&lt;/p&gt;
&lt;p&gt;The second version comes from the maintenance period of &lt;a href=&quot;https://github.com/NeverBehave&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;NeverBehave&lt;/a&gt;. Namespaces were introduced and router.js and radar.js were cut. Routes in the same namespace were concentrated in the same folder and one or more Markdown documents. Lazy loading was also implemented, which greatly improved maintainability and performance. However, it was still scattered in multiple files, and information in different files was also prone to inconsistencies leading to errors.&lt;/p&gt;
&lt;h3 id=&quot;now&quot;&gt;Now&lt;/h3&gt;
&lt;p&gt;This time, routing files are divided into two categories, namespace.ts and routing files with any name.&lt;/p&gt;
&lt;p&gt;namespace.ts will define the namespace information by exporting an object named namespace&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#C74DED&quot;&gt; type&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Namespace&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;@/types&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C74DED&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; namespace&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Namespace&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A1A7CC&quot;&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fields contained in the namespace are restricted via TypeScript to&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Namespace&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  url&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  categories&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string[];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  description&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This information will be compiled and used by documentation and RSSHub Radar&lt;/p&gt;
&lt;p&gt;The routing file defines routing information by exporting an object named route.&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; { &lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;Route&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; } &lt;/span&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#96E072&quot;&gt; &quot;@/types&quot;&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#C74DED&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt; route&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Route&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#A0A1A7CC&quot;&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fields contained in route are restricted via TypeScript to&lt;/p&gt;
&lt;pre class=&quot;astro-code andromeeda&quot; style=&quot;background-color:#23262E;color:#D5CED9; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;ts&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;interface&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Route&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  path&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string[];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  name&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  url&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  maintainers&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string[];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt;  handler&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;ctx&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Context&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#C74DED&quot;&gt;=&amp;gt;&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Promise&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt;Data&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Data&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  example&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  parameters&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#FFE66D&quot;&gt; Record&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;&amp;lt;string, string&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  description&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  categories&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string[];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  features&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    requireConfig&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string[] &lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;|&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; false;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    requirePuppeteer&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; boolean;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    antiCrawler&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; boolean;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    supportRadar&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; boolean;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    supportBT&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; boolean;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    supportPodcast&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; boolean;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    supportScihub&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; boolean;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;  radar&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    source&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string[];&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#00E8C6&quot;&gt;    target&lt;/span&gt;&lt;span style=&quot;color:#EE5D43&quot;&gt;?:&lt;/span&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt; string;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#D5CED9&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Previously, route.js, mantainer.js, radar.js and document information were concentrated in this file, reducing multiple definitions and reducing the possibility of errors.&lt;/p&gt;
&lt;h3 id=&quot;implementation&quot;&gt;Implementation&lt;/h3&gt;
&lt;p&gt;The implementation logic is that the development environment traverses the entire route folder, finds all namespace.ts and routing files, reads information, loads routes, and uses a pre-compiled path list in the generation environment to avoid traversal and unnecessary loading processes. The code is at: &lt;a href=&quot;https://github.com/DIYgod/RSSHub/blob/master/lib/registry.ts&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/blob/master/lib/registry.ts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The document is also created by traversing the route folder, finding all the required information and then synthesizing it into a series of Markdown files. Manual maintenance is no longer required. The code is at: &lt;a href=&quot;https://github.com/DIYgod/RSSHub/blob/master/scripts/workflow/build-routes.ts&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://github.com/DIYgod/RSSHub/blob/master/scripts/workflow/build-routes.ts&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Of course, routes developed using previous routing standards need to be migrated to the new standard rather than abandoned directly. The information has been captured and organized in batches through scripts and replaced. However, especially the documents are confusing and contain many errors, so the captured information also has many errors, which can only be manually modified gradually in the future.&lt;/p&gt;
&lt;h2 id=&quot;future&quot;&gt;future&lt;/h2&gt;
&lt;p&gt;Through this series of improvements, RSSHub can finally throw away its historical baggage and develop new features with peace of mind. Here are some of the ideas I have accumulated to inspire others:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Since RSSHub is a data collection, its use is not necessarily limited to RSS. The JSON output function can be enhanced and used as a general RESTful API. For example, it can provide an interface to get the next page or output non-feed data like Twitter follower count.&lt;/li&gt;
&lt;li&gt;User system and user-defined configuration, generate your own private subscription address &lt;a href=&quot;https://github.com/DIYgod/RSSHub/issues/14706&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;#14706&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Routing error notification and health detection &lt;a href=&quot;https://github.com/DIYgod/RSSHub/issues/14712&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;#14712&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Linkage with RSS3 nodes and cryptocurrency revenue sharing &lt;a href=&quot;https://twitter.com/rss3_/status/1731822029199094012&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;https://twitter.com/rss3_/status/1731822029199094012&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;AI translation and summarization&lt;/li&gt;
&lt;li&gt;More detailed analysis of instance data and reverse derivation of automatically recommended Radar rules&lt;/li&gt;
&lt;li&gt;An RSSHub instance bound to a local browser or client is expected to truly solve the problem of anti-crawling
-…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, open source is a very expensive thing, and RSSHub cannot survive today without the help of these developers.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/DIYgod/RSSHub/graphs/contributors&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/attachments/contributors.svg?width=890&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;and these kind-hearted people who sponsored&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/DIYgod/sponsors&quot; rel=&quot;noopener noreferrer&quot; target=&quot;_blank&quot;&gt;&lt;img src=&quot;https://diygod.cc/_astro/sponsors.wide.BXuDch7i_Zjvw2n.svg&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;auto&quot; width=&quot;1000&quot; height=&quot;723&quot; alt=&quot;&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If RSSHub is helping you, I hope you can actively participate and contribute your own small force to the future of information freedom.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</content:encoded><category>Creations</category></item></channel></rss>