17 |
18 |
19 |
--------------------------------------------------------------------------------
/acknowledgements.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "Acknowledgments")
4 | ◊;(define-meta subtitle "A perspective beyond the hype")
5 | ◊(define-meta published "2020-03-09T21:07:17+01:00")
6 | ◊(define-meta updated "2021-03-10T19:01:43+01:00")
7 | ◊(define-meta uuid "d33953be-5f6c-43b0-a700-eab1e1540447")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | ◊epigraph{
13 | ◊qt[#:author "John Lennon"]{
14 | As usual, there is a great woman behind every idiot.
15 | }
16 | }
17 |
18 | Sometimes, I'm an idiot, but it's clear that I have a great woman in Veronica that supports me, and without her this book would've never seen the light of day.
19 |
20 | A big thanks to Filip Strömbäck, who proof-read everything and provided me with tons of good feedback.
21 |
22 | And thanks to all others who gave me supportive comments, feedback, pull requests and donations. I'm forever grateful.
23 |
24 |
--------------------------------------------------------------------------------
/toc-template.html.p:
--------------------------------------------------------------------------------
1 | ◊(local-require pollen/tag)
2 |
3 |
4 |
5 |
6 | ◊|main-title|
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
◊|main-title|
17 |
◊|subtitle|
18 |
19 |
20 | ◊(->html doc #:splice #t)
21 |
22 |
23 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/sass/homepage.scss:
--------------------------------------------------------------------------------
1 | .homepage {
2 | max-width: 600px;
3 | margin: 0.5rem 1.2rem;
4 | }
5 |
6 | .homepage > header {
7 | margin-bottom: 1em;
8 | padding-bottom: 0.3em;
9 | border-bottom: 0.2em solid;
10 |
11 | h1 {
12 | @extend %sans-serif;
13 | font-size: 2rem;
14 | margin-bottom: 0.2rem;
15 | }
16 | h2 {
17 | @extend %sans-serif;
18 | font-size: 1rem;
19 | font-weight: normal;
20 | font-style: italic;
21 | margin: 0;
22 | margin-left: 0.3rem;
23 | margin-bottom: 0.2rem;
24 | }
25 | }
26 |
27 | .toc section:not(:first-child) {
28 | padding-top: 1.2rem;
29 | }
30 |
31 | .toc {
32 | @extend %sans-serif;
33 | a {
34 | text-decoration: none;
35 | background: none;
36 | text-shadow: none;
37 | }
38 | a:hover {
39 | text-decoration: underline;
40 | }
41 | }
42 |
43 | .toc h1 {
44 | font-size: 1.1rem;
45 | }
46 | .toc ul {
47 | margin-top: 0.5rem;
48 | list-style: none;
49 |
50 | li {
51 | margin-left: 0.6rem;
52 | margin-top: 0.3rem;
53 | }
54 | }
55 | .toc .planned {
56 | color: $planned-chapter-link;
57 | }
58 |
--------------------------------------------------------------------------------
/sass/code-highlight.scss:
--------------------------------------------------------------------------------
1 | // Light Background
2 | $gb-lm-bg0: #fbf1c7;
3 | $gb-lm-bg0-hard: #f9f5d7;
4 | $gb-lm-bg0-soft: #f2e5bc;
5 | $gb-lm-bg1: #ebdbb2;
6 | $gb-lm-bg2: #d5c4a1;
7 | $gb-lm-bg3: #bdae93;
8 | $gb-lm-bg4: #a89984;
9 |
10 | // Light Foreground
11 | // Gruvbox colorscheme
12 | $gb-lm-fg0: #282828;
13 | $gb-lm-fg1: #3c3836;
14 | $gb-lm-fg2: #504945;
15 | $gb-lm-fg3: #665c54;
16 | $gb-lm-fg4: #7c6f64;
17 |
18 | // Light Colors
19 | $gb-lm-dark-red: #cc241d;
20 | $gb-lm-dark-green: #98971a;
21 | $gb-lm-dark-yellow: #d79921;
22 | $gb-lm-dark-blue: #458588;
23 | $gb-lm-dark-purple: #b16286;
24 | $gb-lm-dark-aqua: #689d6a;
25 | $gb-lm-dark-orange: #d65d0e;
26 | $gb-lm-dark-gray: #928374;
27 |
28 | $gb-lm-light-red: #9d0006;
29 | $gb-lm-light-green: #79740e;
30 | $gb-lm-light-yellow: #b57614;
31 | $gb-lm-light-blue: #076678;
32 | $gb-lm-light-purple: #8f3f71;
33 | $gb-lm-light-aqua: #427b58;
34 | $gb-lm-light-orange: #af3a03;
35 | $gb-lm-light-gray: #7c6f64;
36 |
37 | .highlight {
38 | margin: 1rem 0;
39 | border-top: 1px dashed $body-hr-color;
40 | border-bottom: 1px dashed $body-hr-color;
41 |
42 | > pre {
43 | padding: 0 0.6em;
44 | margin: 1em 0;
45 | }
46 |
47 | // Builtin
48 | .nb { color: $gb-lm-light-orange; }
49 | // Strings
50 | .s, .s1, .s2, .sb, .sc, .sd, .sh { color: $gb-lm-light-green}
51 | // Keywords
52 | .k, .kc, .kd, .kn, .kp, .kr { color: $gb-lm-light-blue; }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/sass/colors.scss:
--------------------------------------------------------------------------------
1 | // Common cryptocurrency colors:
2 | $bch-green: #5DCB79;
3 | $bch-green2: #478559;
4 | $btc-orange: #F59332;
5 | $eth-light-gray: #7e8891;
6 | $eth-dark-gray: #343535;
7 | $xmr-orange: #ff6600;
8 | $xmr-gray: #4c4c4c;
9 |
10 | // Used colors
11 | $body-font-color: $eth-dark-gray;
12 | $body-font-color2: #4c4d4d;
13 | $body-background-color: #fcfcfc;
14 | $body-link-hover-color: $btc-orange;
15 | $body-xref-hover-color: $bch-green2;
16 | $body-invalid-iref-hover-color: #db1800;
17 | $body-selection-color: #b4d5fe;
18 | $body-list-sym-color: $xmr-gray;
19 | $body-hr-color: $xmr-gray;
20 | $sidenote-clickable-color: #6087dd;
21 | $email-invalid-color: #b1240c;
22 | $planned-chapter-link: #aaacaf;
23 |
24 | // In graphs:
25 | // http://ksrowell.com/blog-visualizing-data/2012/02/02/optimal-colors-for-graphs/
26 |
27 | // Colors for lines and points:
28 | // #396AB1
29 | // #DA7C30
30 | // #3E9651
31 | // #CC2529
32 | // #535154
33 | // #6B4C9A
34 | // #922428
35 | // #948B3D
36 |
37 | // Colors for bars
38 | // #7293CB
39 | // #E1974C
40 | // #84BA5B
41 | // #D35E60
42 | // #808585
43 | // #9067A7
44 | // #AB6857
45 | // #CCC210
46 |
47 | $excellent: #ccebc5;
48 | $good: #b3cde3;
49 | $poor: #fbb4ae;
50 |
51 | // $legal-moral: #b3e2cd;
52 | // $illegal-immoral: #fdcdac;
53 | // $illegal-moral: #cbd5e8;
54 | // $legal-immoral: #f4cae4;
55 | // These have better green/reds
56 | $illegal-immoral: #fbb4ae;
57 | $illegal-moral: #b3cde3;
58 | $legal-moral: #ccebc5;
59 | $legal-immoral: #decbe4;
60 | // https://colorbrewer2.org/#type=qualitative&scheme=Pastel1&n=6
61 |
62 |
--------------------------------------------------------------------------------
/scripts/bitcoin-keys.py:
--------------------------------------------------------------------------------
1 | import ecdsa
2 | import random
3 | import hashlib
4 |
5 | b58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
6 |
7 | def privateKeyToWif(key_hex):
8 | return base58CheckEncode(0x80, key_hex.decode('hex'))
9 |
10 | def privateKeyToPublicKey(s):
11 | sk = ecdsa.SigningKey.from_string(s.decode('hex'), curve=ecdsa.SECP256k1)
12 | vk = sk.verifying_key
13 | return ('\04' + sk.verifying_key.to_string()).encode('hex')
14 |
15 | def pubKeyToAddr(s):
16 | ripemd160 = hashlib.new('ripemd160')
17 | ripemd160.update(hashlib.sha256(s.decode('hex')).digest())
18 | return base58CheckEncode(0,ripemd160.digest())
19 |
20 | def keyToAddr(s):
21 | return pubKeyToAddr(privateKeyToPublicKey(s))
22 |
23 | def base58encode(n):
24 | result = ''
25 | while n > 0:
26 | result = b58[n%58] + result
27 | n /= 58
28 | return result
29 |
30 | def base58CheckEncode(version, payload):
31 | s = chr(version) + payload
32 | checksum = hashlib.sha256(hashlib.sha256(s).digest()).digest()[0:4]
33 | result = s + checksum
34 | leadingZeros = countLeadingChars(result, '\0')
35 |
36 | return '1' * leadingZeros + base58encode(base256decode(result))
37 |
38 | def base256decode(s):
39 | result = 0
40 | for c in s:
41 | result = result * 256 + ord(c)
42 | return result
43 |
44 | def countLeadingChars(s, ch):
45 | count = 0
46 | for c in s:
47 | if c == ch:
48 | count += 1
49 | else:
50 | break
51 | return count
52 |
53 | private_key = ''.join(['%x' % random.randrange(16) for x in range(0, 64)])
54 | print 'Private key: ',private_key
55 | pubKey = privateKeyToPublicKey(private_key)
56 | print '\nPublic key: ',pubKey
57 | print '\nWif: ',privateKeyToWif(private_key)
58 | print '\nAddress: ',keyToAddr(private_key)
59 |
60 |
--------------------------------------------------------------------------------
/rkt/layout.rkt:
--------------------------------------------------------------------------------
1 | #lang racket/base
2 |
3 | (require "links.rkt")
4 | (require "tags.rkt")
5 |
6 | (provide (all-defined-out))
7 |
8 | (define main-title "Why Cryptocurrencies?")
9 | (define subtitle "What they are, what they do and why they matter")
10 | (define description "An explanation on why cryptocurrencies were created, what they do differently and why they matter. Instead of focusing on the speculative side, we focus on actual use cases of cryptocurrencies and what they can do for us.")
11 |
12 | (define subscription-form
13 | "")
14 | ;; `(form ((action "https://buy.whycryptocurrencies.com/mail/signup")
15 | ;; (method "POST")
16 | ;; (target "_blank")
17 | ;; (class "subscribe"))
18 | ;; (input ((placeholder "Enter your email to join the mailing list")
19 | ;; (class "email")
20 | ;; (value "")
21 | ;; (type "email")
22 | ;; (name "email")))
23 | ;; (input ((id "subscribe")
24 | ;; (class "submit")
25 | ;; (type "submit")
26 | ;; (value "Subscribe")))
27 | ;; ))
28 |
29 | (define follow-section
30 | `(section((class "follow"))
31 | (div ((class "links"))
32 | "Subscribe to the " ,(link feed-url "feed")
33 | ", see the source on " ,(link source-code "Github")
34 | ", " ,(link mailto "contact me")
35 | ", or check out my " ,(link blog-url "blog") ".")
36 | (div ((class "donate"))
37 | "If you appreciate this free book please consider "
38 | ,(link donations "donating")
39 | ".")
40 | ))
41 |
42 | (define (abs-url url)
43 | (format "~a/~a" root-url url))
44 |
45 | (define keywords
46 | "Cryptocurrency,Cryptocurrencies,Blockchain,Bitcoin,Bitcoin Cash,Monero,Ethereum,BTC,BCH,XMR,ETH,ICO,Crypto")
47 |
48 |
--------------------------------------------------------------------------------
/what_is_a_cryptocurrency.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "What is a cryptocurrency?")
4 | ◊(define-meta subtitle "Peer-to-peer electronic cash")
5 | ◊(define-meta published "2019-04-24T20:08:34+01:00")
6 | ◊(define-meta updated "2021-03-10T19:07:32+01:00")
7 | ◊(define-meta uuid "c3deb792-1d85-4a2b-a122-f11361487c82")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | ◊epigraph{
13 | ◊qt[#:author "Nassim Nicholas Taleb"]{
14 | Bitcoin is the beginning of something great: a currency without a government, something necessary and imperative.
15 | }
16 | }
17 |
18 | If we're going to talk about cryptocurrencies, we need to know what they are. Otherwise, how can we tell what value---if any---they have? I think the best description of what a cryptocurrency is can be found in the title of the original white paper: ◊link[bitcoin_whitepaper]{Bitcoin: A Peer-to-Peer Electronic Cash System}. It's like cash, but in digital form.◊mn{read-wp}
19 |
20 | In this part, we'll begin by looking at the properties cryptocurrencies have, which lay the foundation for the use-cases described in the book. Then we'll take a closer look at how cryptocurrencies work. Technical understanding isn't required to see their usefulness, but it helps us navigate the cryptocurrency space and to see through misleading information.
21 |
22 | I'll try to back up my claims that cryptocurrencies are like cash by discussing what money is, the properties of money and if it's fair to classify cryptocurrencies as money. Not only will I proclaim that cryptocurrencies are money, but that they're potentially the best money we've ever seen, although they're held back by volatility and low adoption.
23 |
24 | ◊ndef["read-wp"]{
25 | The white paper is a good read. I recommend you look it up. If you prefer an annotated version or a podcast, there are those as well.
26 | }
27 |
--------------------------------------------------------------------------------
/sync:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use autodie;
4 | use Modern::Perl;
5 | use File::Basename;
6 | use Getopt::Long;
7 |
8 | my $site_bucket = "whycryptocurrencies.com";
9 |
10 | my $help;
11 | my $draft;
12 |
13 | GetOptions(
14 | 'help|h' => \$help,
15 | 'draft' => \$draft,
16 | );
17 |
18 | # Fetch path to current directory
19 | my $root = dirname(__FILE__);
20 | my $site = "_site";
21 |
22 | if ($help) {
23 | help();
24 | }
25 | if ($draft) {
26 | $site_bucket = "cryptobook.hietala.xyz";
27 | say "DRAFT";
28 | }
29 |
30 | say "Publishing site...\n";
31 | system("$root/generate");
32 |
33 | say "Syncing site...\n";
34 |
35 | # Sync media files (cache: 1 week)
36 | say "Syncing images";
37 | sync ($site_bucket, "$site/images", "max-age=604800");
38 |
39 | # Sync fonts (cache: 10 weeks)
40 | say "Syncing fonts";
41 | sync ($site_bucket, "$site/fonts", "max-age=6048000");
42 |
43 | # Sync css (cache: 1 hour)
44 | say "Syncing css";
45 | sync ($site_bucket, "$site/css", "max-age=3600", "-m text/css");
46 |
47 | # Sync rest (cache: 1 hour)
48 | say "Syncing rest";
49 | sync ($site_bucket, "$site/", "max-age=3600, must-revalidate");
50 |
51 | say "Removing deleted";
52 | sync ($site_bucket, "$site/", "", "--delete-removed");
53 |
54 | sub sync {
55 | my $cmd = sync_cmd (@_);
56 | system("$cmd 2>&1");
57 | }
58 |
59 | sub capture_sync {
60 | my $cmd = sync_cmd (@_);
61 | my @output = `$cmd`;
62 | return @output;
63 | }
64 |
65 | sub sync_cmd {
66 | my ($bucket, $dir, $cache, $option) = @_;
67 |
68 | my $header = "";
69 | $header = "--add-header=\"Cache-Control: $cache\"" if $cache;
70 |
71 | $option = "" unless $option;
72 |
73 | return "s3cmd sync -M $option --acl-public $header $dir s3://$bucket/";
74 | }
75 |
76 | sub help {
77 | say "
78 | $0 --draft
79 | sync draft
80 | $0 --help
81 | help
82 | ";
83 |
84 | exit;
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/rkt/index.rkt:
--------------------------------------------------------------------------------
1 | #lang racket/base
2 |
3 | (require pollen/core pollen/tag)
4 | (require "toc.rkt")
5 | (require "tags.rkt")
6 | (require "links.rkt")
7 |
8 | (provide make-toc make-section-nav)
9 |
10 | (define (node page)
11 | (if (pair? page)
12 | (car page)
13 | page))
14 |
15 | (define (children page)
16 | (if (pair? page)
17 | (cdr page)
18 | #f))
19 |
20 | (define (title node)
21 | (if (symbol? node)
22 | (select-from-metas 'title node)
23 | node))
24 | (define (href node)
25 | (if (symbol? node)
26 | (string-append "/" (symbol->string node))
27 | #f))
28 |
29 | (define (make-entry node)
30 | (let ((href (href node))
31 | (title (title node)))
32 | (if href
33 | (make-link #:title title href title)
34 | `(span ((class "planned"))
35 | ,title))))
36 |
37 | (define (make-subnav sub)
38 | (if sub
39 | `(ul
40 | ,@(for/list ([child (in-list sub)])
41 | `(li ,(make-entry child))))
42 | ""))
43 |
44 | (define (page-toc page)
45 | `(section
46 | (h1 ,(make-entry (node page)))
47 | ,(make-subnav (children page))))
48 |
49 | ;; Make a table of content, used on the homepage or dedicated toc page.
50 | (define (make-toc content)
51 | `(nav ((class "toc"))
52 | ,@(map page-toc content)))
53 |
54 | ;; Locate entry in table of content.
55 | (define (toc-entry page)
56 | (findf (λ (x)
57 | (and (pair? x)
58 | (equal? (car x) page)))
59 | toc))
60 |
61 | ;; Make a section navigation page, used on section pages to
62 | ;; list the chapters in that section.
63 | (define (make-section-nav #:section-header? section-header? page)
64 | (define chapters (children (toc-entry page)))
65 | (define header
66 | (if section-header?
67 | `(span ((class "chapters")) "Chapters in this part")
68 | ""))
69 | (if chapters
70 | `(nav ((class "subnav"))
71 | ,header
72 | ,(make-subnav chapters))
73 | ""))
74 |
75 |
--------------------------------------------------------------------------------
/better_digital_payments.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "Better digital payments")
4 | ◊(define-meta subtitle "Benefits over existing payment systems")
5 | ◊(define-meta published "2019-06-03T00:00:00+01:00")
6 | ◊(define-meta updated "2021-05-07T19:11:00+01:00")
7 | ◊(define-meta uuid "011ff2b6-0e56-4501-a6ca-63e9ac6dcc65")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | ◊epigraph{
13 | ◊qt[#:author "Frank Underwood"
14 | #:src "House of Cards"
15 | #:quote-src #t]{
16 | When money is coming your way, you don't ask questions
17 | }
18 | }
19 |
20 | In this part, we'll explore benefits cryptocurrencies have over other digital payment systems, such as VISA, Mastercard, PayPal or Apple Pay.
21 |
22 | The big benefit is cryptocurrencies being ◊def[no-more-third-parties]{permissionless}: anyone can pay and anyone can accept them---for any business. Certain types of businesses, like pot stores or gambling sites, have problems accepting credit cards. Small businesses are also always at risk of having their accounts frozen without warning or recourse.◊mn{minecraft} Similarly, there are people who cannot get bank accounts and are in effect frozen out of large parts of the society.
23 |
24 | But there are other benefits as well. Cryptocurrencies have lower fees and by becoming irreversible very quickly they reduce and often eliminate the risk of ◊def{charge back fraud}, which is a big expense for merchants.
25 |
26 | ◊ndef["minecraft"]{
27 | When I say small business, I do mean it. If PayPal freezes the account of a large or popular business, like ◊link[minecraft-paypal]{freezing the account of Minecraft (2010)}, the social backlash will make sure they're unblocked quickly. Small businesses---who can't gather outrage on social media---will simply be ignored.
28 | }
29 |
30 | ◊(define minecraft-paypal
31 | (x-ref
32 | "2019-06-03"
33 | "http://www.escapistmagazine.com/news/view/103385-PayPal-Freezes-750K-in-MineCraft-Devs-Account"
34 | "Andy Chalk (2010) PayPal Freezes $750K in MineCraft Dev's Account"))
35 |
--------------------------------------------------------------------------------
/rkt/post-process.rkt:
--------------------------------------------------------------------------------
1 | #lang racket/base
2 |
3 | (require txexpr pollen/decode)
4 | (require racket/match racket/list racket/string)
5 |
6 | (provide elem-remover style-remover)
7 |
8 |
9 | ;; Remove all elements with matching class.
10 | (define (elem-remover classes)
11 | (λ (es)
12 | (filter (λ (x)
13 | (not (matches-class x classes)))
14 | es)
15 | ))
16 |
17 | (define (matches-class e classes)
18 | (if (txexpr? e)
19 | (has-class e classes)
20 | #f))
21 |
22 | (define (has-class tx classes)
23 | (define c (attr-ref tx 'class #f))
24 | (if c
25 | (let ((cs (string-split c)))
26 | (not (empty?
27 | (filter (λ (x)
28 | (member x classes))
29 | cs))))
30 | #f))
31 |
32 | (define (remove-attr attrs attr-to-remove)
33 | (filter (λ (x)
34 | (not (eq? (car x) attr-to-remove)))
35 | attrs))
36 |
37 | (define (style-remover tx)
38 | (txexpr (get-tag tx)
39 | (remove-attr (get-attrs tx) 'style)
40 | (get-elements tx)))
41 |
42 |
43 | (module+ test
44 | (require rackunit)
45 | (define remover (elem-remover '("x" "y")))
46 | (check-equal? (has-class `(div "foo") `("x" "y"))
47 | #f)
48 | (check-equal? (has-class `(div ((class "x")) "foo") `("x" "y"))
49 | #t)
50 | (check-equal? (has-class `(div ((class "a x b")) "foo") `("x" "y"))
51 | #t)
52 | (check-equal? (remover
53 | `((div "foo") (div ((class "a")) "bar") (div ((class "x")) "quux")))
54 | `((div "foo") (div ((class "a")) "bar")))
55 | (check-equal? (remover
56 | `("string"))
57 | `("string"))
58 |
59 | (check-equal? (remove-attr `((class "class")) 'style)
60 | '((class "class")))
61 | (check-equal? (remove-attr `((style "style") (class "class")) 'style)
62 | '((class "class")))
63 | (check-equal? (remove-attr `((style "style")) 'style)
64 | '())
65 |
66 | (check-equal? (style-remover `(div ((class "x") (style "style"))))
67 | '(div ((class "x"))))
68 | )
69 |
70 |
--------------------------------------------------------------------------------
/pollen.rkt:
--------------------------------------------------------------------------------
1 | #lang racket/base
2 |
3 | (require txexpr pollen/decode pollen/misc/tutorial pollen/tag)
4 | (require racket/list racket/string)
5 | (require gregor)
6 | (require "rkt/decode.rkt")
7 | (require "rkt/feed.rkt")
8 | (require "rkt/index.rkt")
9 | (require "rkt/layout.rkt")
10 | (require "rkt/links.rkt")
11 | (require "rkt/sidenotes.rkt")
12 | (require "rkt/tags.rkt")
13 | (require "rkt/toc.rkt")
14 | (require "rkt/refs.rkt")
15 |
16 | (provide (all-from-out racket/list racket/string)) ; make util functions available
17 | (provide (all-from-out gregor))
18 | (provide (all-from-out "rkt/decode.rkt"))
19 | (provide (all-from-out "rkt/feed.rkt"))
20 | (provide (all-from-out "rkt/index.rkt"))
21 | (provide (all-from-out "rkt/layout.rkt"))
22 | (provide (all-from-out "rkt/links.rkt"))
23 | (provide (all-from-out "rkt/sidenotes.rkt"))
24 | (provide (all-from-out "rkt/tags.rkt"))
25 | (provide (all-from-out "rkt/toc.rkt"))
26 | (provide (all-from-out "rkt/refs.rkt"))
27 | (provide (all-defined-out))
28 |
29 | (module setup racket/base
30 | (require file/glob)
31 | (require pollen/setup)
32 | (require racket/runtime-path racket/path racket/set)
33 | (require "rkt/index.rkt")
34 |
35 | (provide (all-defined-out))
36 |
37 | (define block-tags (append '(img table tbody tr dt dd dl) default-block-tags))
38 |
39 | ;; Use our own publish script instead...
40 | ;; Ignore stuff during 'raco pollen publish'
41 | ;(define publish-directory "/tmp/why_cryptocurrencies/")
42 | ;(define omitted-paths
43 | ;(list->set (map (λ (sub)
44 | ;(build-path publish-directory sub))
45 | ;`("img-src"
46 | ;"sass"
47 | ;"wip"
48 | ;"rkt"
49 | ;"plots"
50 | ;"clean"
51 | ;"sass-update"
52 | ;"sync"
53 | ;"_site"
54 | ;"generate"
55 | ;"README.md"))))
56 | ;(define omitted-path? (λ (path)
57 | ;(set-member? omitted-paths path)))
58 |
59 | (define rkt-files (glob "rkt/*.rkt"))
60 | (define cache-watchlist rkt-files))
61 |
62 |
--------------------------------------------------------------------------------
/rkt/refs.rkt:
--------------------------------------------------------------------------------
1 | #lang racket/base
2 |
3 | (require racket/string racket/list)
4 | (require "toc.rkt")
5 |
6 | (provide (all-defined-out))
7 |
8 | ; https://guides.libraries.psu.edu/apaquickguide/intext
9 |
10 | (struct href (c url title))
11 | (struct book (author url title))
12 | (struct ch-href (href ref))
13 |
14 | ; (x-ref "2020-01-01" "https://xyz.abc" "description")
15 | (define (x-ref #:class [c #f]
16 | #:ref [ref #f]
17 | date
18 | url
19 | title)
20 | (unless (and (url? url)
21 | (xref? url))
22 | (error (format "bad x-ref url: '~a'" url)))
23 | (when (and (string? date) (string=? date ""))
24 | (error (format "empty date for url: '~a'" url)))
25 | (define alt-text
26 | (if date
27 | (string-append title "\nAccessed " date)
28 | title))
29 | (when ref
30 | (set! url (string-append url "#" (to-name ref))))
31 | (href c url alt-text))
32 |
33 | (define (book-ref url author title)
34 | (unless (url? url)
35 | (error (format "bad book-ref url: '~a'" url)))
36 | (book author url title))
37 |
38 | (define (book-alt-text book)
39 | (string-append (book-title book) " by " (book-author book)))
40 |
41 | (define (book-href book)
42 | (href #f (book-url book) (book-alt-text book)))
43 |
44 | ; (ch-ref 'are_cryptocurrencies_money.html "Are cryptocurrencies money?")
45 | (define (ch-ref #:ref [ref #f]
46 | chapter
47 | title)
48 |
49 | (unless (symbol? chapter)
50 | (error (format "bad ch-ref chapter '~a', expected symbol" href)))
51 |
52 | (unless (in-toc? chapter)
53 | (printf "INVALID CH '~a'~n" chapter))
54 |
55 | (define url (string-append "/" (symbol->string chapter)))
56 | (when ref
57 | (set! url (string-append url "#" (to-name ref))))
58 |
59 | (href #f url title))
60 |
61 | (define (xref? url)
62 | (cond
63 | ((regexp-match #rx"^https?://" url) #t)
64 | (else #f)))
65 |
66 | (define (url? x)
67 | (and
68 | (string? x)
69 | (or
70 | (xref? x)
71 | (regexp-match #rx"^#" x)
72 | (regexp-match #rx"^/" x)
73 | (regexp-match #rx"^mailto:" x))))
74 |
75 | (define (to-name x)
76 | (string-replace (string-downcase x) " " "-"))
77 |
78 |
--------------------------------------------------------------------------------
/template.xml.p:
--------------------------------------------------------------------------------
1 | ◊(define (skip-feed post)
2 | (select-from-metas 'skip-feed post))
3 | ◊(define (title post)
4 | (select-from-metas 'title post))
5 | ◊(define (subtitle post)
6 | (select-from-metas 'subtitle post))
7 | ◊(define (uuid post)
8 | (string-append
9 | "urn:uuid:"
10 | (select-from-metas 'uuid post)))
11 | ◊(define (published post)
12 | (select-from-metas 'published post))
13 | ◊(define (updated post)
14 | (select-from-metas 'updated post))
15 | ◊(define (validate post)
16 | (check-meta 'title post)
17 | (check-meta 'uuid post)
18 | (check-meta 'published post)
19 | (check-meta 'updated post)
20 | post)
21 | ◊(define (check-meta sym post)
22 | (when (not (select-from-metas sym post))
23 | (printf "MISSING REQUIRED META '~a from ~a\n" sym post)))
24 | ◊(define posts
25 | (map validate
26 | (filter (λ (post)
27 | (not (skip-feed post)))
28 | (pagetree->list toc-pagetree))))
29 | ◊(define (entry post)
30 | (->html
31 | `((entry
32 | ,@(entry-content post)
33 | "\n")
34 | "\n")))
35 | ◊(define (entry-content post)
36 | (add-between
37 | (filter-entry-content
38 | `((title ,(title post))
39 | (published ,(published post))
40 | (updated ,(updated post))
41 | (id ,(uuid post))
42 | (link ((href ,(abs-url post))))
43 | (content [[type "html"]] ,(feed-content post))))
44 | ;(summary [[type "html"]] ,(feed-summary post))))
45 | "\n\t"))
46 | ◊(define (filter-entry-content cs)
47 | ; We don't require subtitles be in all posts, this is a general implementation.
48 | (filter (λ (x)
49 | (cadr x))
50 | cs))
51 | ◊(define latest-update
52 | (foldr (λ (a b)
53 | (if (string>? a b)
54 | a
55 | b))
56 | ""
57 | (map updated posts)))
58 |
59 |
60 | ◊|main-title|
61 |
62 |
63 |
64 | Jonas Hietala
65 | ◊|email|
66 |
67 | ◊(uuid here)
68 | ◊|latest-update|
69 | ◊(map entry posts)
70 |
71 |
72 |
--------------------------------------------------------------------------------
/plots/why-unbanked.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import matplotlib as mpl
4 | from matplotlib import patheffects
5 |
6 |
7 | plt.xkcd()
8 |
9 | # Removes remaining white lines after xkcdifying the plot.
10 | # Changing the background didn't fix it.
11 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
12 |
13 | labels = ('Not enough money',
14 | 'Do not need an account',
15 | 'Accounts too expensive',
16 | 'Family member already has an account',
17 | 'Financial institutions too far away',
18 | 'Lack of necessary documentation',
19 | 'Lack of trust',
20 | 'Religious reasons')
21 | other_reasons = (61, 30, 26, 26, 22, 20, 16, 6)
22 | sole_reason = (19, 3, 1, 5, 1, 2, 1, 0)
23 | y_pos = np.arange(len(labels))
24 |
25 | mycol = '#343535'
26 | background_col = '#fcfcfc'
27 |
28 | plt.rcParams["font.family"] = "Concourse T4"
29 | plt.rcParams["axes.linewidth"] = 1
30 | plt.rcParams["axes.edgecolor"] = mycol
31 | plt.rcParams["text.color"] = mycol
32 | plt.rcParams["xtick.color"] = mycol
33 | plt.rcParams["ytick.color"] = mycol
34 | plt.rcParams["ytick.minor.width"] = 0
35 | plt.rcParams['figure.facecolor'] = background_col
36 |
37 |
38 | fig, ax = plt.subplots(figsize=(6, 4.5))
39 | fig.set_facecolor(background_col)
40 | ax.set_facecolor(background_col)
41 |
42 | # http://ksrowell.com/blog-visualizing-data/2012/02/02/optimal-colors-for-graphs/
43 | other_col = '#7293CB'
44 | sole_col = '#D35E60'
45 |
46 | p1 = ax.barh(y_pos, other_reasons, align='center', color=other_col, height=0.5)
47 | p2 = ax.barh(y_pos, sole_reason, align='center', color=sole_col, height=0.54)
48 | ax.set_yticks(y_pos)
49 | ax.set_yticklabels(labels)
50 | ax.invert_yaxis()
51 |
52 | ax.xaxis.set_tick_params(width=1)
53 | ax.yaxis.set_ticks_position('none')
54 |
55 | ax.spines['right'].set_color('none')
56 | ax.spines['top'].set_color('none')
57 | ax.spines['left'].set_color('none')
58 |
59 | plt.legend( (p2[0], p1[0]), ('Cited as sole reason', 'Cited with other reasons') )
60 |
61 | plt.savefig('why-unbanked.svg', format="svg", transparent=False, bbox_inches="tight")
62 | print("done")
63 |
64 | #print plt.style.available
65 | #print plt.rcParams.keys()
66 | #for x in plt.rcParams.keys():
67 | #print x
68 |
--------------------------------------------------------------------------------
/plots/energy-bars.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import matplotlib as mpl
4 | from matplotlib import patheffects
5 |
6 |
7 | plt.xkcd()
8 |
9 | # Removes remaining white lines after xkcdifying the plot.
10 | # Changing the background didn't fix it.
11 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
12 |
13 |
14 | #24.72 TWh Nigeria
15 | #39.5 TWh New Zealand
16 | #46.94 TWh Portugal
17 | #58.2 TWh BTC
18 | #94.23 TWh Kazakhstan
19 | #133 TWh Sweden
20 | #3902 Twh USA
21 | #5564 Twh China
22 |
23 | labels = ('Nigeria',
24 | 'New Zealand',
25 | 'Portugal',
26 | 'BTC',
27 | 'Kazakhstan',
28 | 'Sweden')
29 |
30 | energy = (24.72, 39.5, 46.94, 58.2, 94.23, 133)
31 | y_pos = np.arange(len(labels))
32 |
33 | mycol = '#343535'
34 | background_col = '#fcfcfc'
35 |
36 | plt.rcParams["font.family"] = "Concourse T4"
37 | plt.rcParams["axes.linewidth"] = 1
38 | plt.rcParams["axes.edgecolor"] = mycol
39 | plt.rcParams["text.color"] = mycol
40 | plt.rcParams["xtick.color"] = mycol
41 | plt.rcParams["ytick.color"] = mycol
42 | plt.rcParams["ytick.minor.width"] = 0
43 | plt.rcParams['figure.facecolor'] = background_col
44 |
45 | fig, ax = plt.subplots(figsize=(6, 4.5))
46 | fig.set_facecolor(background_col)
47 | ax.set_facecolor(background_col)
48 |
49 | # http://ksrowell.com/blog-visualizing-data/2012/02/02/optimal-colors-for-graphs/
50 | energy_col = '#7293CB'
51 | btc_col = '#D35E60'
52 |
53 | # barlist=plt.bar([1,2,3,4], [1,2,3,4])
54 | # barlist[0].set_color('r')
55 | # plt.show()
56 |
57 | p1 = ax.barh(y_pos, energy, align='center', color=energy_col, height=0.5)
58 | p1[3].set_color(btc_col)
59 | ax.set_yticks(y_pos)
60 | ax.set_yticklabels(labels)
61 | ax.invert_yaxis()
62 |
63 | ax.xaxis.set_tick_params(width=1)
64 | ax.yaxis.set_ticks_position('none')
65 |
66 | ax.spines['right'].set_color('none')
67 | ax.spines['top'].set_color('none')
68 | ax.spines['left'].set_color('none')
69 | ax.set_xlabel('TWh')
70 |
71 | #plt.legend( (p2[0], p1[0]), ('Cited as sole reason', 'Cited with other reasons') )
72 |
73 | plt.savefig('energy-bars.svg', format="svg", transparent=False, bbox_inches="tight")
74 | print("done")
75 |
76 | #print plt.style.available
77 | #print plt.rcParams.keys()
78 | #for x in plt.rcParams.keys():
79 | #print x
80 |
--------------------------------------------------------------------------------
/better_currency.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "A better currency")
4 | ◊(define-meta subtitle "Magic internet money")
5 | ◊(define-meta published "2019-10-25T00:00:00+01:00")
6 | ◊(define-meta updated "2021-03-10T19:02:34+01:00")
7 | ◊(define-meta uuid "10e625f0-2470-49a9-ae74-36f0c78d943b")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | ◊epigraph{
13 | ◊qt[#:author "Mayer Amschel Rothschild"]{
14 | Let me issue and control a nation's money and I care not who writes the laws.
15 | }
16 | }
17 |
18 | Our focus during the last part was on the use of cryptocurrencies as a payment system. But in fact Bitcoin wasn't invented to be just an improved PayPal---it was made to be something more, with much larger consequences: a completely new form of money.◊mn{currency-money}
19 |
20 | ◊ndef["currency-money"]{
21 | Remember that the term ◊def{money} refers to an intangible concept and ◊def{currency} is the actual thing used as money.
22 |
23 | For example, cryptocurrencies are money---you cannot pay me with "3 cryptocurrency". And Bitcoin (BTC) is a currency because you can send 3 BTC to me.
24 | }
25 |
26 | In this part, we'll focus on the benefits of using cryptocurrencies as money and compare it to what we use today. We'll begin by highlighting flaws with the current financial system, flaws that originate from our use of unsound money. It's a system full of broken incentives that relies on being able to predict the unpredictable and that tries to solve all problems by printing more money, which has various negative side-effects.
27 |
28 | Even though privacy is a human right, the digital money we use today is very bad for privacy as all our financial information is tracked, stored and sold to private companies. Privacy is important because it helps you stay who you are and it increases the personal security for yourself and others.
29 |
30 | Cryptocurrencies mitigate all these problems, and they're also truly global, unhampered by borders, disputes and local monetary policy.◊mn{why-not-gold?}
31 |
32 | ◊ndef["why-not-gold?"]{
33 | Many of my arguments in this part will apply to gold or gold-backed fiat, as well as cryptocurrencies. See the ◊link[comparing-properties]{comparision of properties between different forms of money} for more details on the pros and cons of different forms of money.
34 | }
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/rkt/feed.rkt:
--------------------------------------------------------------------------------
1 | #lang racket/base
2 |
3 | (require txexpr pollen/decode pollen/template/html pollen/core)
4 | (require racket/match racket/list)
5 | (require racket/pretty)
6 | (require "post-process.rkt")
7 |
8 | (provide feed-summary feed-content)
9 |
10 | (define (feed-content post)
11 | ; Remove some elements that don't render so well in feed context.
12 | (define html
13 | (->html (decode-elements
14 | (get-doc post)
15 | #:txexpr-proc style-remover
16 | #:txexpr-elements-proc (elem-remover '("donations"
17 | "subscribe")))
18 | #:splice? #t))
19 | ; Wrap html in CDATA tag as it might ge interpreted as XML tags.
20 | (string-append
21 | ""))
24 |
25 | (define (feed-summary post)
26 | ; Remove some elements that don't render so well in feed context.
27 | (define html
28 | (->html (decode-elements
29 | (summary (get-doc post))
30 | #:txexpr-elements-proc (elem-remover '("donations"
31 | "margin-toggle"
32 | "sidenote"
33 | "marginnote"
34 | "subscribe")))
35 | #:splice? #t))
36 | ; Wrap html in CDATA tag as it might ge interpreted as XML tags.
37 | (string-append
38 | ""))
41 |
42 | ; Define the summary of a post as everything up until the first h2 tag
43 | (define (summary post)
44 | (match post
45 | [(cons 'root xs)
46 | (append
47 | '(root)
48 | (takef xs (λ (x)
49 | (match x
50 | [(cons 'h2 _) #f]
51 | [else #t])))
52 | `((p "...")))]
53 | [else (error "Expected root to begin post")]))
54 |
55 | (module+ test
56 | (require rackunit)
57 | (define post `(root (div "x")
58 | (div (h1 "title") (h2 "subtitle"))
59 | (p "some stuff")
60 | (h2 "TITLE")
61 | (p "rest of stuff")))
62 |
63 | (define expected `(root (div "x")
64 | (div (h1 "title") (h2 "subtitle"))
65 | (p "some stuff")
66 | (p "...")))
67 | (check-equal? (summary post) expected)
68 | )
69 |
70 |
--------------------------------------------------------------------------------
/generate:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import glob
4 | import subprocess
5 | import os.path
6 | import shutil
7 | import errno
8 |
9 | # Can upload with rsync -a -v _site/ /
10 |
11 | # Could extend this with using a cache of sha256 output files
12 | # to see if we should regenerate or not,
13 | # but just cleaning _site/ is easier before build
14 | # and using raco to update during development.
15 | def main():
16 | dirname = os.path.dirname(__file__)
17 | output_dir = os.path.join(dirname, "_site/")
18 |
19 | print("Generating site to", output_dir)
20 |
21 | # First make sure we've generated all files.
22 | for f in glob.glob('*.pm'):
23 | output = os.path.splitext(f)[0]
24 | if os.path.isfile(output):
25 | print("Skip", f)
26 | continue
27 | subprocess.call(["raco", "pollen", "render", f])
28 |
29 | # sassc sass/main.scss --style compressed > css/main.css
30 | with open('css/main.css', 'w') as main:
31 | print("Generating css/main.css")
32 | subprocess.call(["sassc", "sass/main.scss", "--style", "compressed"],
33 | stdout=main)
34 |
35 | # Clean destination directory.
36 | if not os.path.exists(output_dir):
37 | print("Creating", output_dir)
38 | os.makedirs(output_dir)
39 | else:
40 | print("Cleaning", output_dir)
41 | for root, dirs, files in os.walk(output_dir):
42 | for f in files:
43 | os.unlink(os.path.join(root, f))
44 | for d in dirs:
45 | shutil.rmtree(os.path.join(root, d))
46 |
47 | # Then copy all relevant files.
48 | for match in ["*.html", "*.xml", "css/", "files/",
49 | "images/", "fonts/", "favicon*"]:
50 | for src in glob.glob(match):
51 | dst = os.path.join(output_dir, src)
52 | src = os.path.join(dirname, src)
53 | copy(src, dst)
54 | # Need to remove files generated from .p extensions.
55 | # Easier to just do it after than change capture above.
56 | for pfile in glob.glob("*.p"):
57 | actual = os.path.join(output_dir, os.path.splitext(pfile)[0])
58 | if os.path.isfile(actual):
59 | os.unlink(actual)
60 | print("Done")
61 |
62 |
63 | # Copying in python seems... Difficult.
64 | def copy(src, dst):
65 | try:
66 | shutil.copytree(src, dst)
67 | except OSError as exc: # python >2.5
68 | if exc.errno == errno.ENOTDIR:
69 | shutil.copy(src, dst)
70 | else: raise
71 |
72 | if __name__ == '__main__':
73 | main()
74 |
75 |
76 |
--------------------------------------------------------------------------------
/plots/btc-valuation.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | def prices(url):
15 | with open(url, 'r') as infile:
16 | reader = csv.DictReader(infile)
17 |
18 | date = []
19 | price = []
20 | prev_year = 0
21 | for row in reader:
22 | #d = dt.datetime.strptime(row['Date'], '%Y-%m-%d')
23 | d = dt.datetime.strptime(row['\ufeffDate'], '%b %d, %Y')
24 | # if d.year == prev_year:
25 | # continue
26 | # prev_year = d.year
27 | date.append(d)
28 | price.append(float(row['Price'].replace(',', '')))
29 | return (date, price)
30 |
31 | # https://finance.yahoo.com/quote/BTC-USD/history?period1=1279317600&period2=1557439200&interval=1mo&filter=history&frequency=1mo
32 | # (date, price) = prices('BTC-USD.csv')
33 | # https://www.investing.com/crypto/bitcoin/historical-data
34 | (date, price) = prices('data/Bitcoin Historical Data - Investing.com.csv')
35 |
36 | # It's just for the high level understanding. Plus xkcd style is pretty
37 | plt.xkcd()
38 |
39 | mycol = '#343535'
40 | background_col = '#fcfcfc'
41 |
42 | plt.rcParams["font.family"] = "Concourse T4"
43 | plt.rcParams["axes.linewidth"] = 1
44 | plt.rcParams["axes.edgecolor"] = mycol
45 | plt.rcParams["text.color"] = mycol
46 | plt.rcParams["xtick.color"] = mycol
47 | plt.rcParams["ytick.color"] = mycol
48 | plt.rcParams["ytick.minor.width"] = 0
49 | plt.rcParams['figure.facecolor'] = background_col
50 |
51 |
52 | # Removes remaining white lines after xkcdifying the plot.
53 | # Changing the background didn't fix it.
54 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
55 |
56 | fig = plt.figure(figsize=(8, 5))
57 | ax = fig.add_subplot(1, 1, 1)
58 | fig.set_facecolor(background_col)
59 | ax.set_facecolor(background_col)
60 |
61 | def y_fmt(y, pos):
62 | return ' ${:,.0f}'.format(y)
63 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
64 | ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
65 |
66 | ax.spines['right'].set_color('none')
67 | ax.spines['top'].set_color('none')
68 |
69 | ax.xaxis.set_tick_params(width=2)
70 | ax.yaxis.set_tick_params(width=2)
71 |
72 | ax.set_xticks([dt.date(y, 1, 1) for y in [2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021]])
73 | # ax.set_yticks((0, 500, 1000, 1500, 2000))
74 |
75 | plt.plot(date, price, mycol, linewidth=1.5)
76 |
77 | plt.savefig('btc-valuation.svg', format="svg", transparent=False, bbox_inches='tight')
78 | print("done")
79 |
80 |
--------------------------------------------------------------------------------
/plots/data/WID_Data_Metadata/WID_Metadata_29102019-065949.csv:
--------------------------------------------------------------------------------
1 | Downloaded from wid.world on 29-10-2019 at 06:59:49
2 | Country Code;Country Name;WID Variable code;Percentile group;Unit;Variable name;Intuitive description;Technical description;Methodological Notes;Sources
3 | US;USA;sptinc992j;p0p50;share;Pre-tax national income. Share Adults. equal-split adults;Pre-tax national income share held by a given percentile group. Pre-tax national income is the sum of all pre-tax personal income flows accruing to the owners of the production factors, labor and capital, before taking into account the operation of the tax/transfer system, but after taking into account the operation of pension system. The central difference between personal factor income and pre-tax income is the treatment of pensions, which are counted on a contribution basis by factor income and on a distribution basis by pre-tax income. The population is comprised of individuals over age 20. The base unit is the individual (rather than the household) but resources are split equally within couples.;Pre-tax national income =Pre-tax labor income [total pre-tax income ranking]+Pre-tax capital income [total pre-tax income ranking];WID.world computations using wid.world/gpinter.;"[URL][URL_LINK]http://wid.world/document/t-piketty-e-saez-g-zucman-data-appendix-to-distributional-national-accounts-methods-and-estimates-for-the-united-states-2016/[/URL_LINK][URL_TEXT]Piketty, Thomas; Saez, Emmanuel and Zucman, Gabriel (2016). Distributional National Accounts: Methods and Estimates for the United States.[/URL_TEXT][/URL]"
4 | US;USA;sptinc992j;p99p100;share;Pre-tax national income. Share Adults. equal-split adults;Pre-tax national income share held by a given percentile group. Pre-tax national income is the sum of all pre-tax personal income flows accruing to the owners of the production factors, labor and capital, before taking into account the operation of the tax/transfer system, but after taking into account the operation of pension system. The central difference between personal factor income and pre-tax income is the treatment of pensions, which are counted on a contribution basis by factor income and on a distribution basis by pre-tax income. The population is comprised of individuals over age 20. The base unit is the individual (rather than the household) but resources are split equally within couples.;Pre-tax national income =Pre-tax labor income [total pre-tax income ranking]+Pre-tax capital income [total pre-tax income ranking];WID.world computations using wid.world/gpinter.;"[URL][URL_LINK]http://wid.world/document/t-piketty-e-saez-g-zucman-data-appendix-to-distributional-national-accounts-methods-and-estimates-for-the-united-states-2016/[/URL_LINK][URL_TEXT]Piketty, Thomas; Saez, Emmanuel and Zucman, Gabriel (2016). Distributional National Accounts: Methods and Estimates for the United States.[/URL_TEXT][/URL]"
5 |
--------------------------------------------------------------------------------
/plots/data/API_FP.CPI.TOTL.ZG_DS2_en_csv_v2_316099.csv:
--------------------------------------------------------------------------------
1 | "Country Name","Country Code","Indicator Name","Indicator Code","1960","1961","1962","1963","1964","1965","1966","1967","1968","1969","1970","1971","1972","1973","1974","1975","1976","1977","1978","1979","1980","1981","1982","1983","1984","1985","1986","1987","1988","1989","1990","1991","1992","1993","1994","1995","1996","1997","1998","1999","2000","2001","2002","2003","2004","2005","2006","2007","2008","2009","2010","2011","2012","2013","2014","2015","2016","2017","2018",
2 | "Sweden","SWE","Inflation, consumer prices (annual %)","FP.CPI.TOTL.ZG","4.14177915090216","2.15797310981959","4.76619721905829","2.87174034485769","3.3876623108835","5.01277129643832","6.40480825348421","4.28931922993324","1.94312582473236","2.69199458555702","7.01635885100765","7.39554113837636","6.00738969862495","6.71799739819353","9.9117253011324","9.77987398822574","10.2492246529784","11.4418051091076","9.99526541717738","7.20978617047807","13.7063223649004","12.1039385302599","8.58918509047116","8.87302152363218","8.04424238994422","7.37257458126244","4.23591011089793","4.18857151771367","5.82489868439442","6.44327587426601","10.3665533879494","9.44462732944187","2.37436725111879","4.72817304626055","2.15813800724627","2.45514857818396","0.533131978766251","0.658410256243524","-0.267132668354967","0.462175756339131","0.899143734041743","2.40595834145435","2.15848213589263","1.92565534892387","0.373659828721796","0.453170852576176","1.36021468627688","2.21216883436735","3.43704910602875","-0.494460544378046","1.15798802715629","2.96115073822139","0.8883775069237","-0.0442929701486557","-0.17963849411465","-0.0467847449832651","0.984269244577978","1.79449904665596","1.95353530127029",
3 | "United States","USA","Inflation, consumer prices (annual %)","FP.CPI.TOTL.ZG","1.45797598627786","1.07072414764723","1.19877334820185","1.2396694214876","1.27891156462583","1.58516926383669","3.01507537688439","2.77278562259307","4.27179615288534","5.4623862002875","5.83825533848253","4.29276668813045","3.27227824655283","6.17776006377041","11.0548048048048","9.14314686496534","5.74481263549085","6.50168399472839","7.63096383885602","11.2544711292795","13.5492019749684","10.3347153402771","6.13142700027494","3.21243523316063","4.30053547523427","3.54564415209369","1.89804772234275","3.66456321751691","4.07774110744408","4.82700303008949","5.39795643990322","4.23496396453853","3.0288196781497","2.95165696638554","2.6074415921546","2.80541968853655","2.9312041999344","2.33768993730741","1.55227909874362","2.18802719697358","3.37685727149935","2.82617111885402","1.58603162650603","2.27009497336113","2.67723669309173","3.39274684549547","3.22594410070407","2.85267248150136","3.83910029665101","-0.35554626629975","1.64004344238989","3.15684156862206","2.06933726526059","1.46483265562714","1.62222297740821","0.118627135552435","1.26158320570537","2.13011000365963","2.44258329692818",
4 |
--------------------------------------------------------------------------------
/rkt/toc.rkt:
--------------------------------------------------------------------------------
1 | #lang racket/base
2 |
3 | (provide toc toc-pagetree in-toc?)
4 |
5 | (define toc
6 | ;; This replaces the previously hand-made pagetree in index.ptree.
7 | ;; String entries gets removed and are treated as planned chapters.
8 | `(eli5.html
9 | (about_the_book.html
10 | acknowledgements.html
11 | how_to_use.html
12 | free.html
13 | about_me.html)
14 | (what_is_a_cryptocurrency.html
15 | properties_of_a_cryptocurrency.html
16 | how_do_cryptocurrencies_work.html
17 | look_out_for_snake_oil.html
18 | what_is_money.html
19 | are_cryptocurrencies_money.html)
20 | (better_digital_payments.html
21 | cheaper_faster.html
22 | undesirable_businesses.html
23 | freezing_of_merchant_accounts.html
24 | uncensorable_donations.html
25 | for_the_unbanked.html)
26 | (better_currency.html
27 | financial_crisis.html
28 | the_blind_leading_the_blind.html
29 | a_defective_system.html
30 | private_money.html
31 | global_currency.html)
32 | (brave_new_world.html
33 | darknet_markets.html
34 | swiss_bank_account_in_your_pocket.html
35 | cashless_dystopia.html
36 | protection_against_government_confiscation.html
37 | separation_of_money_and_state.html)
38 | (extensions.html
39 | timestamping_service.html
40 | uncensorable_twitter.html
41 | provably_fair_gambling.html
42 | tokens.html
43 | voting.html)
44 | (appendix.html
45 | bitcoin_whitepaper.html
46 | challenges.html
47 | cryptography.html
48 | antifragile.html
49 | ;"Who is Satoshi Nakamoto?"
50 | ;"Is Facebook's Libra a cryptocurrency?"
51 | ;"Further research"
52 | )))
53 |
54 | (define (in-toc? page)
55 | (in-tree? page toc))
56 |
57 | (define (in-tree? x tree)
58 | (cond
59 | [(symbol? tree)
60 | (equal? x tree)]
61 | [(string? tree)
62 | #f]
63 | [(pair? tree)
64 | (or (in-tree? x (car tree))
65 | (in-tree? x (cdr tree)))]
66 | [else #f]))
67 |
68 | (module+ test
69 | (require rackunit)
70 | (check-equal? (in-toc? 'eli5.html) #t)
71 | (check-equal? (in-toc? 'free.html) #t)
72 | (check-equal? (in-toc? 'cheaper_faster.html) #t)
73 | (check-equal? (in-toc? 'blaha) #f)
74 | )
75 |
76 | ;; Take a tree containing unfinished entries as strings
77 | ;; and construct a pagetree out of it.
78 | (define (tree-to-pagetree pt)
79 | (define (transform-elem x)
80 | (if (symbol? x)
81 | x
82 | #f))
83 | (define (transform-pair x)
84 | (let ((head (transform-elem (car x))))
85 | (if head
86 | (cons head (transform-list (cdr x)))
87 | #f)))
88 | (define (transform x)
89 | (if (pair? x)
90 | (transform-pair x)
91 | (transform-elem x)))
92 | (define (transform-list xs)
93 | (filter (λ (x) x)
94 | (map transform xs)))
95 |
96 | `(pagetree-root
97 | ,@(transform-list pt)))
98 |
99 | (define toc-pagetree (tree-to-pagetree toc))
100 |
101 |
--------------------------------------------------------------------------------
/plots/m2.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | def parse(url):
15 | with open(url, 'r') as infile:
16 | reader = csv.DictReader(infile)
17 |
18 | value = []
19 | date = []
20 | for row in reader:
21 | date.append(dt.datetime.strptime(row['DATE'], '%Y-%m-%d'))
22 | value.append(float(row['M2']))
23 | return (date, value)
24 |
25 | # https://fred.stlouisfed.org/series/M2
26 | (date, value) = parse('data/M2.csv')
27 |
28 | # It's just for the high level understanding. Plus xkcd style is pretty
29 | plt.xkcd()
30 |
31 | mycol = '#343535'
32 | background_col = '#fcfcfc'
33 |
34 | plt.rcParams["font.family"] = "Concourse T4"
35 | plt.rcParams["axes.linewidth"] = 1
36 | plt.rcParams["axes.edgecolor"] = mycol
37 | plt.rcParams["text.color"] = mycol
38 | plt.rcParams["xtick.color"] = mycol
39 | plt.rcParams["ytick.color"] = mycol
40 | plt.rcParams["ytick.minor.width"] = 0
41 | plt.rcParams['figure.facecolor'] = background_col
42 |
43 | # Removes remaining white lines after xkcdifying the plot.
44 | # Changing the background didn't fix it.
45 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
46 |
47 | fig = plt.figure(figsize=(8, 5))
48 | ax = fig.add_subplot(1, 1, 1)
49 | fig.set_facecolor(background_col)
50 | ax.set_facecolor(background_col)
51 |
52 | def y_fmt(y, pos):
53 | return '${:,.0f}'.format(int(y/1000))
54 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
55 | ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
56 | ax.set_ylabel("Supply in trillions")
57 |
58 | ax.spines['right'].set_color('none')
59 | ax.spines['top'].set_color('none')
60 |
61 | ax.xaxis.set_tick_params(width=2)
62 | ax.yaxis.set_tick_params(width=2)
63 |
64 | # ax.set_xlim(dt.datetime(1965, 01, 01), dt.datetime(2020, 04, 01))
65 | # ax.set_xticks([dt.datetime(y, 01, 01) for y in range(2010, 2021)])
66 | ax.set_xticks([dt.datetime(y, 1, 1) for y in range(2010, 2021, 2)])
67 | # ax.set_ylim(0, 27000000)
68 |
69 | # plt.annotate(
70 | # 'The 2008 financial crisis',
71 | # xy=(dt.datetime(2006, 1, 1), 9700000),
72 | # arrowprops=dict(arrowstyle='->'),
73 | # xytext=(dt.datetime(1990, 1, 1), 12500000))
74 | #
75 | plt.annotate(
76 | 'COVID-19 pandemic',
77 | xy=(dt.datetime(2019, 10, 1), 15500),
78 | arrowprops=dict(arrowstyle='->'),
79 | xytext=(dt.datetime(2014, 1, 1), 16000))
80 |
81 | # #396AB1
82 | # #DA7C30
83 | # #3E9651
84 | # #CC2529
85 | # #535154
86 | # #6B4C9A
87 | # #922428
88 | # #948B3D
89 |
90 | # For different y-axis:
91 | # https://stackoverflow.com/questions/9103166/multiple-axis-in-matplotlib-with-different-scales
92 | plt.plot(date, value, mycol, label='Value')
93 |
94 | plt.savefig('m2.svg', format="svg", transparent=False, bbox_inches='tight')
95 | print("done")
96 |
97 |
--------------------------------------------------------------------------------
/plots/usa-debt.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | def parse(url):
15 | with open(url, 'r') as infile:
16 | reader = csv.DictReader(infile)
17 |
18 | value = []
19 | date = []
20 | for row in reader:
21 | date.append(dt.datetime.strptime(row['DATE'], '%Y-%m-%d'))
22 | value.append(float(row['GFDEBTN']))
23 | return (date, value)
24 |
25 | # https://www.investopedia.com/updates/usa-national-debt/
26 | # https://fred.stlouisfed.org/graph/?id=GFDEBTN,
27 | (date, value) = parse('data/usa-debt.csv')
28 |
29 | # It's just for the high level understanding. Plus xkcd style is pretty
30 | plt.xkcd()
31 |
32 | mycol = '#343535'
33 | background_col = '#fcfcfc'
34 |
35 | plt.rcParams["font.family"] = "Concourse T4"
36 | plt.rcParams["axes.linewidth"] = 1
37 | plt.rcParams["axes.edgecolor"] = mycol
38 | plt.rcParams["text.color"] = mycol
39 | plt.rcParams["xtick.color"] = mycol
40 | plt.rcParams["ytick.color"] = mycol
41 | plt.rcParams["ytick.minor.width"] = 0
42 | plt.rcParams['figure.facecolor'] = background_col
43 |
44 | # Removes remaining white lines after xkcdifying the plot.
45 | # Changing the background didn't fix it.
46 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
47 |
48 | fig = plt.figure(figsize=(8, 5))
49 | ax = fig.add_subplot(1, 1, 1)
50 | fig.set_facecolor(background_col)
51 | ax.set_facecolor(background_col)
52 |
53 | def y_fmt(y, pos):
54 | return '${:,.0f}'.format(int(y/1000000))
55 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
56 | ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
57 | ax.set_ylabel("Debt in trillions")
58 |
59 | ax.spines['right'].set_color('none')
60 | ax.spines['top'].set_color('none')
61 |
62 | ax.xaxis.set_tick_params(width=2)
63 | ax.yaxis.set_tick_params(width=2)
64 |
65 | ax.set_xlim(dt.datetime(1965, 1, 1), dt.datetime(2020, 4, 1))
66 | ax.set_xticks([dt.datetime(y, 1, 1) for y in [1970, 1980, 1990, 2000, 2010, 2020]])
67 | ax.set_ylim(0, 27000000)
68 |
69 | plt.annotate(
70 | 'The 2008 financial crisis',
71 | xy=(dt.datetime(2006, 1, 1), 9700000),
72 | arrowprops=dict(arrowstyle='->'),
73 | xytext=(dt.datetime(1990, 1, 1), 12500000))
74 |
75 | plt.annotate(
76 | 'COVID-19 pandemic',
77 | xy=(dt.datetime(2018, 1, 1), 22000000),
78 | arrowprops=dict(arrowstyle='->'),
79 | xytext=(dt.datetime(2000, 1, 1), 24000000))
80 |
81 | # #396AB1
82 | # #DA7C30
83 | # #3E9651
84 | # #CC2529
85 | # #535154
86 | # #6B4C9A
87 | # #922428
88 | # #948B3D
89 |
90 | # For different y-axis:
91 | # https://stackoverflow.com/questions/9103166/multiple-axis-in-matplotlib-with-different-scales
92 | plt.plot(date, value, mycol, label='Value')
93 |
94 | plt.savefig('usa-debt.svg', format="svg", transparent=False, bbox_inches='tight')
95 | print("done")
96 |
97 |
--------------------------------------------------------------------------------
/how_to_use.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "How to use this book")
4 | ◊(define-meta subtitle "Just some minor things to keep in mind")
5 | ◊(define-meta published "2019-04-12T00:00:00+01:00")
6 | ◊(define-meta updated "2020-10-09T19:49:52+02:00")
7 | ◊(define-meta uuid "a69e0036-a749-4f7c-b02e-dbcfa2776540")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | Each chapter is supposed to be standalone so you can jump around and read it however you want. The only prerequisite is to have a basic understanding of what a cryptocurrency does, which is described in ◊link[what_is_a_cryptocurrency]{the first part}, but if you want you can skip that as well and go back later if needed.
13 |
14 | This book is a work in progress, so I'll add and rework chapters as I go. If you want to keep up to date you have three options:
15 |
16 | ◊ul{
17 | ◊li{Subscribe to the ◊link[feed-url]{feed}}
18 | ◊li{Follow the changes on ◊link[source-code]{Github}}
19 | ◊li{Join the mailing list
20 |
21 | ◊|subscription-form|}
22 | }
23 |
24 | I use ◊link[mbtype]{custom fonts} hosted locally on this site. While fonts are subjective the styling assumes you have these fonts and if you block them---like ◊link[noscript]{noscript} does---it might look slightly off.◊mn{good-idea}
25 |
26 | ◊ndef["good-idea"]{
27 | If you don't use noscript or ad-blockers, I ◊strong{highly} recommend you do. ◊link[ublock]{uBlock Origin} is an excellent ad-blocker (works on Firefox on mobile as well).
28 | }
29 |
30 | External links are highlighted green on hover and internal links are highlighted bright orange. For example compare the external link to ◊link[source-code]{Github} with the link to the ◊link[toc-url]{home page}. There is no tracking on this site but there may very well be on external sites, this serves as a small signifier for those who cares. It also makes it easier to download the site and read it offline.
31 |
32 | Links to the previous and the next chapter can be found at the bottom as well as at the edge of the screen (hover on the edges to see).
33 |
34 | I use sidenotes heavily throughout the text. Like footnotes they use numbered references in the text◊sn{sn} but are placed to the right or below the text instead of at the end of the chapters.
35 |
36 | ◊ndef["sn"]{
37 | This is a sidenote. They're inspired by ◊link[tufte]{Tufte CSS}, feel free to check out the ◊link[source-code]{source of this site} as well if you're curious.
38 | }
39 | ◊ndef["mn"]{
40 | This is a marginnote, they are sidenotes without a numbered reference.
41 | }
42 |
43 | While I do my very best to avoid errors I don't have an editor, only some close friends who help out. Therefore I would be most grateful for any feedback, ideas or error corrections you the reader may find. You can ◊link[mailto]{email me} or create a pull request or an issue ◊link[source-code]{on Github}.
44 |
45 | ◊(define tufte
46 | (x-ref
47 | "2019-04-12"
48 | "https://edwardtufte.github.io/tufte-css/"
49 | "Dave Liepmann: Tufte CSS"))
50 | ◊(define ublock
51 | (x-ref
52 | #f
53 | "https://github.com/gorhill/uBlock"
54 | "uBlock Origin: An efficient blocker for Chromium and Firefox. Fast and lean."))
55 |
56 |
--------------------------------------------------------------------------------
/free.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "Completely free")
4 | ◊(define-meta subtitle "Read it online for free without cost of any kind")
5 | ◊(define-meta published "2019-04-12T00:00:00+01:00")
6 | ◊(define-meta updated "2020-02-18T07:31:14+01:00")
7 | ◊(define-meta uuid "4ede8ca8-fd2e-4112-9d00-e383f94e1a01")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | This book is completely free for you to read online and will be so forever. There will be no ads, no tracking and no crypto mining in the browser.◊mn{browser-mining}
13 |
14 | For all you ◊link[noscript]{noscript} users the site will work perfectly well without JavaScript---in fact I aim to avoid it altogether. I do use ◊link[mbtype]{custom fonts} hosted on this site which noscript blocks by default.
15 |
16 | When I some day finish the book and decide to release it in other formats I may charge for them, but the web version will always be free. If you find the book interesting and want to show appreciation the best way is to share it with others.
17 |
18 | ◊subhead{Donations is a perfect use for cryptocurrencies}
19 |
20 | You can also send donations from anywhere in the world ◊link[cheaper_faster]{nearly instantly with small fees} and ◊link[uncensorable_donations]{no possibility of censorship}. This isn't possible with anything else.
21 |
22 | If you want you can try it out (click to show address and QR code):
23 |
24 | ◊section[#:class "donations"]{
25 | ◊crypto["/images/donations/btc.svg"
26 | "1KDBPbyVhiqotSgnUJzFbdfjn3hUsSgq6W"]{
27 | Bitcoin (BTC)
28 | }
29 | ◊crypto["/images/donations/bch.svg"
30 | "bitcoincash:qqpunsxkhpkuqw4wgpy3tfkqd3ur2ens0gtzf3lzdy"]{
31 | Bitcoin Cash (BCH)
32 | }
33 | ◊crypto["/images/donations/xmr.svg"
34 | "45T1KEsECksHE2ngx7m8FRFb8rEBcDz2w37vP7Jt7sNXZfQBZWmKn2pRKBuiefGGYvjQ7amk4gvFCamzisvdh9qhUZpjbeg"]{
35 | Monero (XMR)
36 | }
37 | ◊crypto["/images/donations/dgc.svg"
38 | "dogecoin:DRJXgnQi51Jt9SyyEjSGb3YCo5FNTDCdWd"]{
39 | Dogecoin (DGC)
40 | }
41 | ◊crypto["/images/donations/ltc.svg"
42 | "litecoin:ltc1q6vfj9emfnygeqdwmvr27pr7v8gjvq24f7s369f"]{
43 | Litecoin (LTC)
44 | }
45 | ◊crypto["/images/donations/eth.svg"
46 | "0xBb98A61EEbd3c7C3108d09228582a28C6297B87D"]{
47 | Ethereum (ETH)
48 | }
49 | }
50 |
51 | ◊ndef["browser-mining"]{
52 | Some sites have replaced ads on their sites and instead use visitors' computers to mine for them. A novel way to use cryptocurrencies but with questionable efficiency. Some do this without asking or notifying you which is definitely scammy. Therefore many adblockers block them outright.
53 | }
54 |
55 |
56 | ◊(define (crypto qr address name)
57 | (define id (string-append "donate-"
58 | (string-downcase (second (regexp-match #px"\\((\\w+)\\)" name)))))
59 | `(div ((class "donation"))
60 | (label ((class "donation-label") (for ,id)) ,name)
61 | (input ((id ,id) (class "donation-toggle") (type "checkbox")))
62 | (span ((class "donation-content"))
63 | (div ((class "address")) ,address)
64 | (img ((class "qr") (src ,qr))))))
65 |
66 |
--------------------------------------------------------------------------------
/indesign-clean:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 |
3 | import glob
4 | import subprocess
5 | import os.path
6 | import re
7 | import sys
8 | import datetime
9 |
10 | dirname = os.path.dirname(__file__)
11 | site_dir = os.path.join(dirname, "_site")
12 |
13 | clean_dir = os.path.join(dirname, "_cleaned")
14 |
15 | def main():
16 | clean()
17 | setup_posts()
18 |
19 | def clean():
20 | subprocess.run(f"rm -r {clean_dir}/", shell=True)
21 | subprocess.run(f"mkdir -p {clean_dir}/", shell=True)
22 |
23 | def setup_static_pages():
24 | subprocess.run(f"cp {ebook_files_dir}/*.xhtml {oebps_dir}", shell=True)
25 |
26 | def setup_posts():
27 | for f in glob.glob(f"{site_dir}/**.html"):
28 | if os.path.isfile(f):
29 | setup_post(f)
30 |
31 | def setup_post(post):
32 | name = os.path.basename(post)
33 | (name, ext) = os.path.splitext(name)
34 | dst = f'{clean_dir}/{name}.xml'
35 | with open(post, 'r') as f:
36 | data = f.read()
37 |
38 | data = replace_post_data(data)
39 |
40 | with open(dst, 'w') as f:
41 | f.write(data)
42 |
43 | def replace_post_data(data):
44 | data = clean_up_xhtml(data)
45 | return data
46 |
47 | # From stuffwithstuff
48 | # https://journal.stuffwithstuff.com/2014/11/03/bringing-my-web-book-to-print-and-ebook/
49 | def clean_up_code_xml(code):
50 | # Ditch most code formatting tags.
51 | code = re.sub(r'([^<]+)',
52 | r"\2", code)
53 |
54 | # Turn comments into something InDesign can map to a style.
55 | code = re.sub(r'([^<]+)',
56 | r"\2", code)
57 |
58 | def clean_up_xhtml(html):
59 | # Ditch newlines in the middle of blocks of text. Out of sheer malice,
60 | # even though they are meaningless in actual XML, InDesign treats them
61 | # as significant.
62 | html = re.sub(r"\n(? <", "><")
67 |
68 | # Re-add newlines after closing paragraph-level tags.
69 | html = html.replace("", "\n")
70 | html = html.replace("", "\n")
71 | html = html.replace("", "\n")
72 | html = html.replace("", "\n")
73 | html = html.replace("", "\n")
74 | html = html.replace("", "\n")
75 | html = html.replace("", "\n")
76 | html = html.replace("", "\n")
77 | html = html.replace("", "\n")
78 | html = html.replace("", "\n")
79 | html = html.replace("", "\n")
80 | html = html.replace("", "\n")
81 |
82 | # Nuke unsupported entities.
83 | html = html.replace(" ", " ")
84 | html = re.sub(r'[^<]+', r"", html)
85 | html = html.replace("Cheaper & faster", "Cheaper & faster")
86 |
87 | # InDesign only recognizes actual tags, not classes.
88 | html = re.sub(r'([^<]+)',
89 | r"\1", html)
90 |
91 | return html
92 |
93 | if __name__ == '__main__':
94 | main()
95 |
96 |
--------------------------------------------------------------------------------
/bitcoin_whitepaper.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "The Bitcoin white paper")
4 | ◊(define-meta subtitle "What started it all")
5 | ◊(define-meta published "2019-04-12T00:00:00+01:00")
6 | ◊(define-meta updated "2021-05-07T19:11:00+01:00")
7 | ◊(define-meta uuid "581c4917-d862-49cb-9d01-495a9106081b")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | ◊epigraph{
13 | ◊qt[#:author "Satoshi Nakamoto"
14 | #:src "Bitcoin: A Peer-to-Peer Electronic Cash System"
15 | #:url bitcoin-pdf
16 | #:quote-src #t]{
17 | A purely peer-to-peer version of electronic cash would allow online payments to be sent directly from one party to another without going through a financial institution. Digital signatures provide part of the solution, but the main benefits are lost if a trusted third party is still required to prevent double-spending.
18 | }
19 | }
20 |
21 |
22 | ◊div[#:class "whitepaper"]{
23 | ◊link[bitcoin-pdf]{Bitcoin: A Peer-to-Peer Electronic Cash System}
24 | }
25 |
26 | The white paper is surprisingly easy to read and I highly recommend you read it. If you prefer a simplified explanation with annotations I can recommend ◊link[ann-wp]{this guide}. If you would rather have it in podcast form ◊link[pod-wp]{here's one}.
27 |
28 | Note that the white paper was created in 2008 and some terminology and implementation details have changed. For example ◊em{nodes} in the paper refer to mining nodes, while today most people run nodes but don't mine. But the high level description is just as true today more than 10 years later.
29 |
30 |
31 | ◊subhead{Alterations}
32 |
33 | I'm sad I have to include this but there have been suggestions to ◊link[alter-wp]{alter the white paper hosted on bitcoin.org}. Rewriting an academic paper and rewriting Bitcoin's history for personal business interests is of course completely unacceptable, but here we are.
34 |
35 | Therefore you might want to make sure you're reading the original unaltered white paper. There are those who ◊link[track-alterations]{try to keep track} of the different versions found online but it's always best to do it yourself. You can compare the PDF's SHA-256 hash with this:
36 |
37 | ◊code{b1674191a88ec5cdd733e4240a81803105dc412d6c6708d53ab94fc248f4f553}
38 |
39 | If you want to go even further the white paper is also embedded in the blockchain which guarantees nobody can change it. Instructions available ◊link[wp-blockchain]{on stackexchange}.
40 |
41 |
42 | ◊(define ann-wp
43 | (x-ref
44 | "2020-04-19"
45 | "https://www.bitcoin.com/get-started/bitcoin-white-paper-beginner-guide/"
46 | "bitcoin.com: Bitcoin Whitepaper: A Beginner's Guide"))
47 | ◊(define pod-wp
48 | (x-ref
49 | "2020-04-19"
50 | "https://bitcoinnews.com/news/bitcoinnews-com-daily-podcast-5th-november-2018-the-bitcoin-white-paper/"
51 | "BitcoinNews.com (2018) BitcoinNews.com Daily Podcast 5th November 2018: The Bitcoin White Paper"))
52 | ◊(define alter-wp
53 | (x-ref
54 | "2020-04-19"
55 | "https://github.com/bitcoin-dot-org/bitcoin.org/issues/1325"
56 | "Cobra-Bitcoin (2016) Amendments to the Bitcoin paper"))
57 | ◊(define track-alterations
58 | (x-ref
59 | "2020-04-19"
60 | "https://blockchair.com/bitcoin/whitepaper/"
61 | "Blockchair: Bitcoin / Whitepaper"))
62 |
63 |
--------------------------------------------------------------------------------
/plots/crisis-us-gdp.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | gdp = [4.5, # 1st 2005
15 | 1.9,
16 | 3.6,
17 | 2.6,
18 | 5.4,
19 | 0.9,
20 | 0.6,
21 | 3.5, # 4th 2006
22 | 0.9, # 1st 2007
23 | 2.3, # 2nd 2007
24 | 2.2, # 3rd 2007
25 | 2.5, # 4th 2007
26 | -2.3, # 1st 2008
27 | 2.1, # 2nd 2008
28 | -2.1, # 3rd 2008
29 | -8.4, # 4th 2008
30 | -4.4, # 1st 2009
31 | -0.6, # 2nd 2009
32 | 1.5, # 3rd 2009
33 | 4.5, # 4th 2009
34 | 1.5,
35 | 3.7,
36 | 3,
37 | 2,
38 | -1,
39 | 2.9,
40 | -0.1,
41 | 4.7] #4th 2011
42 | years = [2005.125,
43 | 2005.375,
44 | 2005.625,
45 | 2005.875,
46 | 2006.125,
47 | 2006.375,
48 | 2006.625,
49 | 2006.875,
50 | 2007.125,
51 | 2007.375,
52 | 2007.625,
53 | 2007.875,
54 | 2008.125,
55 | 2008.375,
56 | 2008.625,
57 | 2008.875,
58 | 2009.125,
59 | 2009.375,
60 | 2009.625,
61 | 2009.875,
62 | 2010.125,
63 | 2010.375,
64 | 2010.625,
65 | 2010.875,
66 | 2011.125,
67 | 2011.375,
68 | 2011.625,
69 | 2011.875,
70 | ]
71 |
72 | # It's just for the high level understanding. Plus xkcd style is pretty
73 | plt.xkcd()
74 |
75 | mycol = '#343535'
76 | background_col = '#fcfcfc'
77 |
78 | plt.rcParams["font.family"] = "Concourse T4"
79 | plt.rcParams["axes.linewidth"] = 2
80 | plt.rcParams["axes.edgecolor"] = mycol
81 | plt.rcParams["text.color"] = mycol
82 | plt.rcParams["xtick.color"] = mycol
83 | plt.rcParams["ytick.color"] = mycol
84 | plt.rcParams["ytick.minor.width"] = 0
85 | plt.rcParams['figure.facecolor'] = background_col
86 |
87 | # Removes remaining white lines after xkcdifying the plot.
88 | # Changing the background didn't fix it.
89 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
90 |
91 | fig = plt.figure(figsize=(10, 5))
92 | ax = fig.add_subplot(1, 1, 1)
93 | fig.set_facecolor(background_col)
94 | ax.set_facecolor(background_col)
95 |
96 | def y_fmt(y, pos):
97 | return '{:,.1f}%'.format(y)
98 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
99 | # ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
100 |
101 | ax.spines['right'].set_color('none')
102 | ax.spines['top'].set_color('none')
103 |
104 | ax.xaxis.set_tick_params(width=2)
105 | ax.yaxis.set_tick_params(width=2)
106 |
107 | # ax.set_xticks([dt.date(y, 1, 1) for y in [1970, 1980, 1990, 2000, 2010, 2018]])
108 | ax.set_xticks([2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012])
109 | #ax.set_yticks((0, 500, 1000, 1500, 2000))
110 |
111 | plt.plot(years, gdp, '#343535')
112 | # plt.plot(date, price, '#343535')
113 |
114 | plt.savefig('us-gdp.svg', format="svg", transparent=False, bbox_inches='tight')
115 | print("done")
116 |
117 |
--------------------------------------------------------------------------------
/plots/gold-valuation.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | # def prices(url):
15 | # with open(url, 'r') as infile:
16 | # reader = csv.DictReader(infile)
17 |
18 | # date = []
19 | # price = []
20 | # for row in reader:
21 | # d = dt.datetime.strptime(row['Date'], '%Y-%m')
22 | # if d.year < 1970:
23 | # continue
24 | # date.append(d)
25 | # price.append(float(row['Price']))
26 | # return (date, price)
27 |
28 | # (date, price) = prices('monthly_gold_prices.csv')
29 |
30 | # # https://www.macrotrends.net/1333/historical-gold-prices-100-year-chart
31 | years = [2021, 2020, 2019, 2018, 2017, 2016, 2015, 2014, 2013, 2012, 2011, 2010, 2009, 2008, 2007, 2006,
32 | 2005, 2004, 2003, 2002, 2001, 2000, 1999, 1998, 1997, 1996, 1995, 1994, 1993, 1992, 1991, 1990,
33 | 1989, 1988, 1987, 1986, 1985, 1984, 1983, 1982, 1981, 1980, 1979, 1978, 1977, 1976, 1975, 1974,
34 | 1973, 1972, 1971, 1970]
35 | closing_price = [1849.70, 1895.10, 1523.00, 1281.65, 1296.50, 1151.70, 1060.20, 1199.25, 1201.50, 1664.00, 1574.50,
36 | 1410.25, 1104.00, 865.00, 836.50, 635.70, 513.00, 438.00, 417.25, 342.75, 276.50, 272.65, 290.85,
37 | 287.45, 289.20, 369.55, 386.70, 382.50, 390.65, 332.90, 353.40, 391.00, 401.00, 410.15, 486.50,
38 | 390.90, 327.00, 309.00, 381.50, 448.00, 400.00, 589.50, 524.00, 224.50, 165.60, 134.55, 140.25,
39 | 187.50, 112.25, 64.70, 43.50, 37.38]
40 |
41 | # It's just for the high level understanding. Plus xkcd style is pretty
42 | plt.xkcd()
43 |
44 | mycol = '#343535'
45 | background_col = '#fcfcfc'
46 |
47 | plt.rcParams["font.family"] = "Concourse T4"
48 | plt.rcParams["axes.linewidth"] = 2
49 | plt.rcParams["axes.edgecolor"] = mycol
50 | plt.rcParams["text.color"] = mycol
51 | plt.rcParams["xtick.color"] = mycol
52 | plt.rcParams["ytick.color"] = mycol
53 | plt.rcParams["ytick.minor.width"] = 0
54 | plt.rcParams['figure.facecolor'] = background_col
55 |
56 | # Removes remaining white lines after xkcdifying the plot.
57 | # Changing the background didn't fix it.
58 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
59 |
60 | fig = plt.figure(figsize=(7, 5))
61 | ax = fig.add_subplot(1, 1, 1)
62 | fig.set_facecolor(background_col)
63 | ax.set_facecolor(background_col)
64 |
65 | def y_fmt(y, pos):
66 | return '${:,.0f}'.format(y)
67 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
68 | # ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
69 |
70 | ax.spines['right'].set_color('none')
71 | ax.spines['top'].set_color('none')
72 |
73 | ax.xaxis.set_tick_params(width=2)
74 | ax.yaxis.set_tick_params(width=2)
75 |
76 | # ax.set_xticks([dt.date(y, 1, 1) for y in [1970, 1980, 1990, 2000, 2010, 2018]])
77 | ax.set_xticks([1970, 1980, 1990, 2000, 2010, 2021])
78 | ax.set_yticks((0, 500, 1000, 1500, 2000))
79 |
80 | plt.plot(years, closing_price, '#343535')
81 | # plt.plot(date, price, '#343535')
82 |
83 | plt.savefig('gold-valuation.svg', format="svg", transparent=False, bbox_inches='tight')
84 | print("done")
85 |
86 |
--------------------------------------------------------------------------------
/brave_new_world.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "Brave new world")
4 | ◊(define-meta subtitle "The world is changing")
5 | ◊(define-meta published "2019-12-15T00:00:00+01:00")
6 | ◊(define-meta updated "2020-10-09T19:51:35+02:00")
7 | ◊(define-meta uuid "dbc67487-8838-42cf-a8b8-fc982cf9142b")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | ◊epigraph{
13 | ◊qt[#:author "J. Robert Oppenheimer"
14 | #:src "1965 TV Broadcast"
15 | #:url oppenheimer]{
16 | We knew the world would not be the same. A few people laughed, a few people cried. Most people were silent. I remembered the line from the Hindu scripture, the Bhagavad Gita; Vishnu is trying to persuade the Prince that he should do his duty and, to impress him, takes on his multi-armed form and says, "Now I am become Death, the destroyer of worlds." I suppose we all thought that, one way or another.
17 | }
18 | }
19 |
20 | ◊(define oppenheimer
21 | (x-ref
22 | "2019-12-15"
23 | "https://www.youtube.com/watch?v=xYPXIfwVyts"
24 | "YouTube (2008) I am become Death (1 min)"))
25 |
26 | In previous parts we've looked at how cryptocurrencies provide ◊link[better_digital_payments]{better digital payments} and why they might be ◊link[better_currency]{better currencies}. This part focuses on some ways the world is changing, and what role cryptocurrencies may play.
27 |
28 | Even if we'd collectively agree that the world would be better off without cryptocurrencies, we cannot make them disappear. We cannot "uninvent" them and they're extremely difficult to shut down. Therefore, like with nuclear bombs, we're stuck with them and have to adapt to a new world.◊mn{destroyer-of-worlds}
29 |
30 | ◊ndef["destroyer-of-worlds"]{
31 | There's an excellent ◊link[hh-destroyer]{Hardcore History episode} on the topic of nuclear weapons and how humanity has been balancing on the edge of destruction ever since their invention.
32 | }
33 |
34 | Cryptocurrencies won't make the world a better place in all respects. For example untraceable digital money makes it easier for people to demand ransoms, such as ◊link[cryptolocker]{CryptoLocker} which locks down your computer and demands payment in order to unlock it.
35 |
36 | Other things skirt the lines between legality and morality, such as the darknet markets that makes it easy to trade any type of goods. Or how cryptocurrencies allow you to store any amount of money, hidden from the watchful eyes of the tax collectors.
37 |
38 | While cryptocurrencies in some ways force the world to change, they can also protect us from the changing world. For instance the move towards a cashless society will spell disaster for everyone without access to digital money. But cryptocurrencies are for everyone, and they can shield us from tracking and seizures by the government.◊mn{friend-or-foe?}
39 |
40 | ◊ndef["friend-or-foe?"]{
41 | Governments can be our friend, but they can also be our foe as well.
42 | }
43 |
44 | And cryptocurrencies have the potential for something even bigger. For the first time in history there's the possibility to separate control of money from the state. This would be a historic shift that would cause massive change worldwide. Of course this is speculation on my part, and even if it were to happen it probably wouldn't be during our lifetime.
45 |
46 | But one things seems clear to me. The Pandora's Box has been opened, and the world will never be the same again.
47 |
48 | ◊(define hh-destroyer
49 | (x-ref
50 | "2019-12-15"
51 | "https://www.dancarlin.com/hardcore-history-59-the-destroyer-of-worlds/"
52 | "Dan Carlin (2017) The Destroyer of Worlds"))
53 | ◊(define cryptolocker
54 | (x-ref
55 | "2019-12-15"
56 | "https://en.wikipedia.org/wiki/CryptoLocker"
57 | "Wikipedia: Cryptolocker"))
58 |
59 |
--------------------------------------------------------------------------------
/plots/inflation.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | def parse(url):
15 | with open(url, 'r') as infile:
16 | reader = csv.DictReader(infile)
17 |
18 | inflation = []
19 | value = []
20 | years = range(1960, 2019)
21 | for row in reader:
22 | country = row['Country Code']
23 | if country == "SWE":
24 | (inflation, value) = parse_row(row, years)
25 | return (inflation, value, years)
26 |
27 | def parse_row(row, years):
28 | val = 100.0
29 | infl = []
30 | value = []
31 | for x in years:
32 | value.append(val)
33 | inflation = float(row[str(x)])
34 | infl.append(inflation)
35 | val -= val * inflation/100.0
36 | return (infl, value)
37 |
38 | # https://data.worldbank.org/indicator/FP.CPI.TOTL?locations=US-SE-XC
39 | (inflation, value, years) = parse('data/API_FP.CPI.TOTL.ZG_DS2_en_csv_v2_316099.csv')
40 |
41 | for x, y in zip(years, value):
42 | print(x, y)
43 |
44 | # It's just for the high level understanding. Plus xkcd style is pretty
45 | plt.xkcd()
46 |
47 | mycol = '#343535'
48 | background_col = '#fcfcfc'
49 |
50 | plt.rcParams["font.family"] = "Concourse T4"
51 | plt.rcParams["axes.linewidth"] = 1
52 | plt.rcParams["axes.edgecolor"] = mycol
53 | plt.rcParams["text.color"] = mycol
54 | plt.rcParams["xtick.color"] = mycol
55 | plt.rcParams["ytick.color"] = mycol
56 | plt.rcParams["ytick.minor.width"] = 0
57 | plt.rcParams['figure.facecolor'] = background_col
58 |
59 | # Removes remaining white lines after xkcdifying the plot.
60 | # Changing the background didn't fix it.
61 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
62 |
63 | fig = plt.figure(figsize=(8, 5))
64 | ax = fig.add_subplot(1, 1, 1)
65 | fig.set_facecolor(background_col)
66 | ax.set_facecolor(background_col)
67 |
68 | # def y_fmt(y, pos):
69 | # return ' ${:,.0f}'.format(y)
70 | # ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
71 | # ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
72 |
73 | ax.spines['right'].set_color('none')
74 | ax.spines['top'].set_color('none')
75 |
76 | ax.xaxis.set_tick_params(width=2)
77 | ax.yaxis.set_tick_params(width=2)
78 |
79 | # ax.set_xticks([dt.date(y, 1, 1) for y in [1970, 1980, 1990, 2000, 2010, 2018]])
80 | #ax.set_yticks((0, 25, 50, 75, 100, 125))
81 | ax.set_xlim(1960, 2020)
82 | ax.set_xticks((1960, 1970, 1980, 1990, 2000, 2010, 2018))
83 | ax.set_ylim(0, 105)
84 | ax.set_yticks((0, 20, 40, 60, 80, 100))
85 |
86 | # #396AB1
87 | # #DA7C30
88 | # #3E9651
89 | # #CC2529
90 | # #535154
91 | # #6B4C9A
92 | # #922428
93 | # #948B3D
94 |
95 | par1 = ax.twinx()
96 | par1.set_ylim(-2, 15)
97 | par1.set_ylabel("Inflation rate")
98 | def y_fmt(y, pos):
99 | return '{:,.1f}%'.format(y)
100 | par1.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
101 |
102 | par1.spines['top'].set_color('none')
103 | par1.yaxis.set_tick_params(width=2)
104 |
105 | ax.set_ylabel("Value")
106 |
107 | #print(value)
108 |
109 | # For different y-axis:
110 | # https://stackoverflow.com/questions/9103166/multiple-axis-in-matplotlib-with-different-scales
111 | p1, = ax.plot(years, value, '#CC2529', label='Value')
112 | p2, = par1.plot(years, inflation, '#6B4C9A', label='Inflation rate')
113 |
114 | ax.yaxis.label.set_color(p1.get_color())
115 | par1.yaxis.label.set_color(p2.get_color())
116 |
117 | ax.legend(handles=[p1, p2], loc='best')
118 |
119 | plt.savefig('inflation.svg', format="svg", transparent=False, bbox_inches='tight')
120 | print("done")
121 |
122 |
--------------------------------------------------------------------------------
/plots/wealth-inequality.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | def parse(url):
15 | with open(url, 'r') as infile:
16 | reader = csv.DictReader(infile, delimiter=';')
17 |
18 | top1 = []
19 | top1_year = []
20 | middle40 = []
21 | middle40_year = []
22 | for row in reader:
23 | print(row)
24 | v = row['shweal_z_US']
25 | if v:
26 | percentile = row['Percentile']
27 | if percentile == 'p99p100':
28 | top1.append(float(v))
29 | top1_year.append(int(row['Year']))
30 | elif percentile == 'p50p90':
31 | middle40.append(float(v))
32 | middle40_year.append(int(row['Year']))
33 | else:
34 | exit("bad percentile: " + percentile)
35 | #date.append(dt.datetime.strptime(row['Date'], '%b %Y'))
36 | #value.append(float(row['Federal debt']))
37 | return (top1, top1_year, middle40, middle40_year)
38 |
39 | # https://wid.world/
40 | (top1, top1_year, middle40, middle40_year) = parse('data/WID_Data_Metadata/WID_Data_29102019-070448.csv')
41 |
42 | # It's just for the high level understanding. Plus xkcd style is pretty
43 | plt.xkcd()
44 |
45 | mycol = '#343535'
46 | background_col = '#fcfcfc'
47 |
48 | plt.rcParams["font.family"] = "Concourse T4"
49 | plt.rcParams["axes.linewidth"] = 1
50 | plt.rcParams["axes.edgecolor"] = mycol
51 | plt.rcParams["text.color"] = mycol
52 | plt.rcParams["xtick.color"] = mycol
53 | plt.rcParams["ytick.color"] = mycol
54 | plt.rcParams["ytick.minor.width"] = 0
55 | plt.rcParams['figure.facecolor'] = background_col
56 |
57 | # Removes remaining white lines after xkcdifying the plot.
58 | # Changing the background didn't fix it.
59 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
60 |
61 | fig = plt.figure(figsize=(8, 5))
62 | ax = fig.add_subplot(1, 1, 1)
63 | fig.set_facecolor(background_col)
64 | ax.set_facecolor(background_col)
65 |
66 | def y_fmt(y, pos):
67 | return ' {:,.0f}%'.format(y*100)
68 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
69 | # ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
70 |
71 | # def y_fmt(y, pos):
72 | # return '${:,.0f}'.format(int(y/1000000))
73 | # ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
74 | #ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
75 | #ax.set_ylabel("Debt in trillions")
76 |
77 | ax.spines['right'].set_color('none')
78 | ax.spines['top'].set_color('none')
79 |
80 | ax.xaxis.set_tick_params(width=2)
81 | ax.yaxis.set_tick_params(width=2)
82 |
83 | # ax.set_xticks([dt.date(y, 1, 1) for y in [1970, 1980, 1990, 2000, 2010, 2018]])
84 | #ax.set_yticks((0, 25, 50, 75, 100, 125))
85 | ax.set_ylim(0.2, 0.4)
86 | ax.set_xlim(1962, 2015)
87 | ax.set_xticks((1962, 1970, 1980, 1990, 2000, 2010))
88 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1966, 1980, 1990, 2000, 2010, 2019]])
89 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1970, 1980, 1990, 2000, 2010, 2019]])
90 | #ax.set_ylim(0, 25000000)
91 | #ax.set_yticks((0, 20, 40, 60, 80, 100))
92 |
93 | # #396AB1
94 | # #DA7C30
95 | # #3E9651
96 | # #CC2529
97 | # #535154
98 | # #6B4C9A
99 | # #922428
100 | # #948B3D
101 |
102 | # For different y-axis:
103 | # https://stackoverflow.com/questions/9103166/multiple-axis-in-matplotlib-with-different-scales
104 | ax.plot(top1_year, top1, '#6B4C9A', label='Top 1%')
105 | ax.plot(middle40_year, middle40, '#CC2529', label='Middle 40%')
106 |
107 | ax.legend(loc='best')
108 |
109 | plt.savefig('wealth-inequality.svg', format="svg", transparent=False, bbox_inches='tight')
110 | print("done")
111 |
112 |
--------------------------------------------------------------------------------
/plots/emission-rates.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import matplotlib.pyplot as plt
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 |
9 | import matplotlib.patches as mpatches
10 |
11 | def btc(x):
12 | """ Calculate BTC supply """
13 | total = 0
14 | # Start with 50 BTC/block
15 | block_reward = 50
16 | # Half every 210 000 block (approx every 4 years)
17 | halving = 210000
18 |
19 | supply = []
20 | for i in range(0, x[-1] + 1):
21 | if i > 0 and i % halving == 0:
22 | block_reward /= 2
23 |
24 | total += block_reward
25 | supply.append(total)
26 |
27 | return [supply[i] for i in x]
28 |
29 |
30 | def xmr(x):
31 | """ This just doesn't work right... """
32 | total = 0.0
33 | M = 2**(64 - 1)
34 |
35 | supply = []
36 | for i in range(0, x[-1] + 1):
37 | A = int(total * 10**12)
38 | block_reward = max(0.6, math.floor((M - A)/(2**19)) / 10**12)
39 |
40 | total += block_reward
41 | supply.append(total)
42 |
43 | return [supply[i] for i in x]
44 |
45 |
46 | # Use actual blocktime of 9.4 min
47 | btc_years = np.arange(2009, 2051, 1)
48 | btc_blocks = [int((y - 2009)*365*24*60/9.4) for y in btc_years]
49 | btc_supply = btc(btc_blocks)
50 |
51 | # Just approximate values... It's not exact anyway.
52 | xmr_x = [2014, 2015, 2016, 2017, 2018, 2019,
53 | 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2027, 2028, 2029, 2030, 2031, 2032, 2033,
54 | 2034, 2035, 2036, 2037, 2038, 2039, 2040, 2041, 2042, 2043, 2044, 2045, 2046, 2047,
55 | 2048, 2049, 2050]
56 | xmr_y = [0, 6967312, 11319909, 14382647, 15671138, 16849637, 17460360, 17679028, 17850042,
57 | 18007722, 18165402, 18323082, 18480762, 18638442, 18796122, 18953802, 19111482,
58 | 19269162, 19426842, 19584522, 19742202, 19899882, 20057562, 20215242, 20372922,
59 | 20530602, 20688282, 20845962, 21003642, 21161322, 21319002, 21476682, 21634362,
60 | 21792042, 21949722, 22107402, 22265082]
61 |
62 |
63 | # It's just for the high level understanding. Plus xkcd style is pretty
64 | plt.xkcd()
65 |
66 | mycol = '#343535'
67 | background_col = '#fcfcfc'
68 |
69 | plt.rcParams["font.family"] = "Concourse T4"
70 | plt.rcParams["axes.linewidth"] = 1
71 | plt.rcParams["axes.edgecolor"] = mycol
72 | plt.rcParams["text.color"] = mycol
73 | plt.rcParams["xtick.color"] = mycol
74 | plt.rcParams["ytick.color"] = mycol
75 | plt.rcParams["ytick.minor.width"] = 0
76 | plt.rcParams['figure.facecolor'] = background_col
77 |
78 | # Removes remaining white lines after xkcdifying the plot.
79 | # Changing the background didn't fix it.
80 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
81 |
82 | fig = plt.figure(figsize=(8, 5))
83 | ax = fig.add_subplot(1, 1, 1)
84 | fig.set_facecolor(background_col)
85 | ax.set_facecolor(background_col)
86 |
87 | def y_fmt(y, pos):
88 | return '{val:d} M'.format(val=int(y/1000000))
89 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
90 |
91 | ax.spines['right'].set_color('none')
92 | ax.spines['top'].set_color('none')
93 |
94 | ax.set_xticks((2009, 2014, 2020, 2030, 2040, 2050))
95 | ax.set_yticks((0, 10000000, 21000000))
96 | ax.tick_params(axis=u'both', which=u'both',width=2)
97 |
98 | plt.annotate(
99 | 'we are here',
100 | xy=(2020, 16800000), arrowprops=dict(arrowstyle='->'), xytext=(2025, 13100000))
101 |
102 | # http://ksrowell.com/blog-visualizing-data/2012/02/02/optimal-colors-for-graphs/
103 | #396AB1
104 | #DA7C30
105 |
106 |
107 | btc_col = '#DA7C30'
108 | monero_col = '#396AB1'
109 | #monero_col = '#CC2529'
110 | plt.plot(btc_years, btc_supply, btc_col, label='Bitcoin')
111 | plt.plot(xmr_x, xmr_y, monero_col, label='Monero')
112 |
113 | plt.legend(loc='lower right')
114 |
115 | plt.figtext(0.03, 0.91, 'Circulating supply')
116 |
117 | plt.savefig('emission-rates.svg', format="svg", transparent=False, bbox_inches='tight')
118 | print("done")
119 |
120 |
--------------------------------------------------------------------------------
/plots/income-inequality.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | def parse(url):
15 | with open(url, 'r') as infile:
16 | reader = csv.DictReader(infile, delimiter=';')
17 |
18 | top1 = []
19 | top1_year = []
20 | bottom50 = []
21 | bottom50_year = []
22 | for row in reader:
23 | print(row)
24 | v = row['sptinc_992_j_US']
25 | if v:
26 | percentile = row['Percentile']
27 | if percentile == 'p99p100':
28 | top1.append(float(v))
29 | top1_year.append(int(row['Year']))
30 | elif percentile == 'p0p50':
31 | bottom50.append(float(v))
32 | bottom50_year.append(int(row['Year']))
33 | else:
34 | exit("bad percentile: " + percentile)
35 | #date.append(dt.datetime.strptime(row['Date'], '%b %Y'))
36 | #value.append(float(row['Federal debt']))
37 | return (top1, top1_year, bottom50, bottom50_year)
38 |
39 | # https://wid.world/
40 | (top1, top1_year, bottom50, bottom50_year) = parse('data/WID_Data_Metadata/WID_Data_29102019-065949.csv')
41 |
42 | # It's just for the high level understanding. Plus xkcd style is pretty
43 | plt.xkcd()
44 |
45 | mycol = '#343535'
46 | background_col = '#fcfcfc'
47 |
48 | plt.rcParams["font.family"] = "Concourse T4"
49 | plt.rcParams["axes.linewidth"] = 1
50 | plt.rcParams["axes.edgecolor"] = mycol
51 | plt.rcParams["text.color"] = mycol
52 | plt.rcParams["xtick.color"] = mycol
53 | plt.rcParams["ytick.color"] = mycol
54 | plt.rcParams["ytick.minor.width"] = 0
55 | plt.rcParams['figure.facecolor'] = background_col
56 |
57 | # Removes remaining white lines after xkcdifying the plot.
58 | # Changing the background didn't fix it.
59 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
60 |
61 | fig = plt.figure(figsize=(8, 5))
62 | ax = fig.add_subplot(1, 1, 1)
63 | fig.set_facecolor(background_col)
64 | ax.set_facecolor(background_col)
65 |
66 | def y_fmt(y, pos):
67 | return ' {:,.0f}%'.format(y*100)
68 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
69 | # ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
70 |
71 | # def y_fmt(y, pos):
72 | # return '${:,.0f}'.format(int(y/1000000))
73 | # ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
74 | #ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
75 | #ax.set_ylabel("Debt in trillions")
76 |
77 | ax.spines['right'].set_color('none')
78 | ax.spines['top'].set_color('none')
79 |
80 | ax.xaxis.set_tick_params(width=2)
81 | ax.yaxis.set_tick_params(width=2)
82 |
83 | # ax.set_xticks([dt.date(y, 1, 1) for y in [1970, 1980, 1990, 2000, 2010, 2018]])
84 | #ax.set_yticks((0, 25, 50, 75, 100, 125))
85 | ax.set_ylim(0.1, 0.25)
86 | ax.set_xlim(1962, 2015)
87 | ax.set_xticks((1962, 1970, 1980, 1990, 2000, 2010))
88 | #ax.set_xlim(dt.datetime(1965, 01, 01), dt.datetime(2019, 01, 31))
89 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1966, 1970, 1975, 1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019]])
90 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1966, 1980, 1990, 2000, 2010, 2019]])
91 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1970, 1980, 1990, 2000, 2010, 2019]])
92 | #ax.set_ylim(0, 25000000)
93 | #ax.set_yticks((0, 20, 40, 60, 80, 100))
94 |
95 | # #396AB1
96 | # #DA7C30
97 | # #3E9651
98 | # #CC2529
99 | # #535154
100 | # #6B4C9A
101 | # #922428
102 | # #948B3D
103 |
104 | # For different y-axis:
105 | # https://stackoverflow.com/questions/9103166/multiple-axis-in-matplotlib-with-different-scales
106 | ax.plot(top1_year, top1, '#6B4C9A', label='Top 1%')
107 | ax.plot(bottom50_year, bottom50, '#CC2529', label='Bottom 50%')
108 |
109 | ax.legend(loc='best')
110 |
111 | plt.savefig('income-inequality.svg', format="svg", transparent=False, bbox_inches='tight')
112 | print("done")
113 |
114 |
--------------------------------------------------------------------------------
/sass/fonts.scss:
--------------------------------------------------------------------------------
1 | $century-supra: 'century_supra_a';
2 | @font-face {
3 | font-family: $century-supra;
4 | font-style: normal;
5 | font-weight: normal;
6 | font-stretch: normal;
7 | font-display: auto;
8 | src: url('/fonts/century_supra_a_regular.woff2') format('woff2');
9 | }
10 | @font-face {
11 | font-family: $century-supra;
12 | font-style: italic;
13 | font-weight: normal;
14 | font-stretch: normal;
15 | font-display: auto;
16 | src: url('/fonts/century_supra_a_italic.woff2') format('woff2');
17 | }
18 | @font-face {
19 | font-family: $century-supra;
20 | font-style: normal;
21 | font-weight: bold;
22 | font-stretch: normal;
23 | font-display: auto;
24 | src: url('/fonts/century_supra_a_bold.woff2') format('woff2');
25 | }
26 | @font-face {
27 | font-family: $century-supra;
28 | font-style: italic;
29 | font-weight: bold;
30 | font-stretch: normal;
31 | font-display: auto;
32 | src: url('/fonts/century_supra_a_bold_italic.woff2') format('woff2');
33 | }
34 |
35 | $concourse: 'concourse_4';
36 | @font-face {
37 | font-family: $concourse;
38 | font-style: normal;
39 | font-weight: normal;
40 | font-stretch: normal;
41 | font-display: auto;
42 | src: url('/fonts/concourse_4_regular.woff2') format('woff2');
43 | }
44 | @font-face {
45 | font-family: $concourse;
46 | font-style: italic;
47 | font-weight: normal;
48 | font-stretch: normal;
49 | font-display: auto;
50 | src: url('/fonts/concourse_4_italic.woff2') format('woff2');
51 | }
52 | @font-face {
53 | font-family: $concourse;
54 | font-style: normal;
55 | font-weight: bold;
56 | font-stretch: normal;
57 | font-display: auto;
58 | src: url('/fonts/concourse_4_bold.woff2') format('woff2');
59 | }
60 | @font-face {
61 | font-family: $concourse;
62 | font-style: italic;
63 | font-weight: bold;
64 | font-stretch: normal;
65 | font-display: auto;
66 | src: url('/fonts/concourse_4_bold_italic.woff2') format('woff2');
67 | }
68 |
69 | $triplicate: 'Triplicate';
70 | @font-face {
71 | font-family: $triplicate;
72 | font-style: normal;
73 | font-weight: normal;
74 | font-stretch: normal;
75 | font-display: auto;
76 | src: url('/fonts/triplicate_a_code_regular.woff2') format('woff2');
77 | }
78 | @font-face {
79 | font-family: $triplicate;
80 | font-style: italic;
81 | font-weight: normal;
82 | font-stretch: normal;
83 | font-display: auto;
84 | src: url('/fonts/triplicate_a_code_italic.woff2') format('woff2');
85 | }
86 | @font-face {
87 | font-family: $triplicate;
88 | font-style: normal;
89 | font-weight: bold;
90 | font-stretch: normal;
91 | font-display: auto;
92 | src: url('/fonts/triplicate_a_code_bold.woff2') format('woff2');
93 | }
94 | @font-face {
95 | font-family: $triplicate;
96 | font-style: italic;
97 | font-weight: bold;
98 | font-stretch: normal;
99 | font-display: auto;
100 | src: url('/fonts/triplicate_a_code_bold_italic.woff2') format('woff2');
101 | }
102 |
103 |
104 | $serif-font-family: $century-supra, Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
105 | $sans-serif-font-family: $concourse, Frutiger, "Frutiger Linotype", Univers, Calibri, "Gill Sans", "Gill Sans MT", "Myriad Pro", Myriad, "DejaVu Sans Condensed", "Liberation Sans", "Nimbus Sans L", Tahoma, Geneva, "Helvetica Neue", Helvetica, Arial, sans-serif;
106 | $code-font-family: $triplicate, Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace;
107 |
108 |
109 | %sans-serif {
110 | text-rendering: optimizeLegibility;
111 | font-family: $sans-serif-font-family;
112 | font-feature-settings: "onum" off, "kern" on;
113 | }
114 | %sans-serif-tnum {
115 | text-rendering: optimizeLegibility;
116 | @extend %sans-serif;
117 | font-feature-settings: 'kern' on, 'tnum' on;
118 | }
119 |
120 | %serif {
121 | text-rendering: optimizeLegibility;
122 | font-family: $serif-font-family;
123 | font-feature-settings: "kern" on;
124 | }
125 |
126 |
127 |
--------------------------------------------------------------------------------
/eli5.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "Why cryptocurrencies in five minutes")
4 | ◊(define-meta subtitle "ELI5 - what is the inherent values of cryptocurrencies?")
5 | ◊(define-meta published "2019-07-17T00:00:00+01:00")
6 | ◊(define-meta updated "2021-05-07T19:11:00+01:00")
7 | ◊(define-meta uuid "cef34868-0317-4c6f-8b9f-cfb66464f2a8")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | While cryptocurrencies are mostly seen as speculative assets or get rich quick schemes, they have valuable properties and other valuable use cases. For example:
13 |
14 | ◊ul{
15 | ◊li{◊strong{Excellent monetary properties}
16 |
17 | Cryptocurrencies ◊link[are_cryptocurrencies_money]{have better monetary properties} than anything else in history. In contrast to the fiat money we use today, cryptocurrencies have a limited supply and compared to gold, cryptocurrencies are much more portable, are easier to divide into small parts, and cannot be counterfeit.
18 |
19 | At first glance, this may seem insignificant, but money affects everything and even small improvements can have a massive effect.
20 | }
21 | ◊li{◊strong{Cheaper payments}
22 |
23 | Merchants have to pay a 1--4% fee for every credit card transaction, while cryptocurrency transactions only come with a ◊link[fee-comparisons]{small fixed fee.}◊mn{bitcoin-fees}
24 |
25 | ◊note-pos[#:top -2]{bitcoin-fees}
26 | }
27 | ◊li{◊strong{Irreversible digital transactions}
28 |
29 | You receive money in ◊link[speed-comparisons]{under an hour}, and after that the money is yours. In contrast, it may take days to receive other digital payments that can also be reversed weeks or months later.
30 |
31 | Thus merchants don't have to worry about having a purchase reversed, which usually means they have to swallow the loss.◊mn{chargeback-fraud}
32 |
33 | ◊note-pos[#:top -2]{chargeback-fraud}
34 | }
35 | ◊li{◊strong{For anyone and anything}
36 |
37 | Cryptocurrencies can be used by anyone. It's for businesses ◊link[undesirable_businesses]{who cannot accept credit cards}, for people ◊link[for_the_unbanked]{without a bank account}, and people in dysfunctional countries. You can use it for truly ◊link[uncensorable_donations]{uncensorable donations}, and you don't have to worry that your payment processor or bank will ◊link[freezing_of_merchant_accounts]{freeze your account}.
38 |
39 | Nobody can prevent you from sending or receiving cryptocurrencies.
40 | }
41 | ◊li{◊strong{Financial privacy}
42 |
43 | Banks, credit card companies, and payment processors have all your financial transactions on record. Cryptocurrencies allow you to ◊link[private_money]{reclaim some of your privacy} as they work like a ◊link[swiss_bank_account_in_your_pocket]{swiss bank account in your pocket}.◊mn{bitcoin-privacy}
44 |
45 | ◊note-pos[#:top -2]{bitcoin-privacy}
46 | }
47 | ◊li{◊strong{An alternative financial system}
48 |
49 | The traditional financial system rewards behavior that caused the ◊link[financial_crisis]{2008 financial crisis} and relies on being able to ◊link[the_blind_leading_the_blind]{predict the unpredictable}. Cryptocurrencies represent an alternative without a central authority that can manipulate the money supply, and they can be used to truly ◊link[separation_of_money_and_state]{separate money from state}.
50 | }
51 | ◊li{◊strong{Extensions}
52 |
53 | You can build applications on top of cryptocurrencies, such as ◊link[provably_fair_gambling]{provably fair gambling} or a ◊link[timestamping_service]{timestamping service} based on mathematics instead of social proof.
54 | }
55 | }
56 |
57 | Of course cryptocurrencies don't perfectly solve everything, and there are many difficulties---both technical and social---that need to be resolved. As with all new technology, they will be associated with positive and negative change.
58 |
59 | If you want to learn more and see more examples, just continue reading. You can also ◊link[toc-url]{jump} to whatever chapter interests you---they're supposed to be self-contained.
60 |
61 | ◊ndef["bitcoin-fees"]{
62 | If you've heard about the ridiculously high Bitcoin fees, then don't worry---it's the exception not the rule. Please read the chapter ◊link[cheaper_faster]{Cheaper & faster} for more info.
63 | }
64 |
65 | ◊ndef["bitcoin-privacy"]{
66 | Please note that Bitcoin (and most other cryptocurrencies) are only ◊def{pseudo-anonymous}. Please see ◊link[privacy-challenge]{the privacy and fungibility challenge} for more information.
67 | }
68 |
69 | ◊ndef["chargeback-fraud"]{
70 | This is known as ◊def[charge-back-fraud]{charge back fraud} or ◊def{friendly fraud} and is a big problem for merchants.
71 | }
72 |
73 |
74 |
--------------------------------------------------------------------------------
/about_me.html.pm:
--------------------------------------------------------------------------------
1 | #lang pollen
2 |
3 | ◊(define-meta title "About me, the author")
4 | ◊(define-meta subtitle "Hello, I'm Jonas")
5 | ◊(define-meta published "2019-04-12T00:00:00+01:00")
6 | ◊(define-meta updated "2021-05-07T19:11:00+01:00")
7 | ◊(define-meta uuid "b1aaa891-a7c6-4bf4-a01c-4c43c0446abf")
8 | ◊(define-meta template "chapter.html")
9 |
10 | ◊(clear-sidenotes)
11 |
12 | ◊epigraph{
13 | ◊qt[#:author "Albert Einstein "]{
14 | I have no special talents. I am only passionately curious.
15 | }
16 | }
17 |
18 | I wonder what you might think of someone who is writing a book about cryptocurrencies? Maybe you'll think of a teenage multi-millionaire or an old neckbeard raving about free software? Or perhaps an anarchist who's trying to convince you that "governments are evil" and that "taxation is theft"?◊mn{there-are}
19 |
20 | ◊ndef["there-are"]{
21 | It's easy to see why cryptocurrencies might attract people like this. Cryptocurrencies are all about freedom, and the free software movement (saying that ◊strong{all} software should be free) and anarchism seek freedom to the extreme.
22 | }
23 |
24 | I am none of those. I'm just a normal person, perhaps a bit introverted. I only have a short beard, and I pay my taxes and enjoy the benefits we get here in Sweden. Unfortunately, I'm neither a teenager nor a multi-millionaire.
25 |
26 | I first heard about Bitcoin in 2010, about one year after its creation. I installed a Bitcoin wallet and played around with it a little. It was just another internet thing for me and like many others, it failed to hold my attention. After about 10 or 15 minutes, I removed it and promptly forgot about it.◊mn{had-some}
27 |
28 | ◊ndef["had-some"]{
29 | I did have a couple of Bitcoins at that time. It hurts to admit this as today a single Bitcoin is worth around $50,000, but sadly I've lost them. After all, back then they weren't worth anything.
30 | }
31 |
32 | It wasn't until four years later that I looked at Bitcoin again. I studied computer science and we had a course in cryptography---a very fun course I might add---with a part about cryptocurrencies. We went through the technical details, and it made me curious about Bitcoin on a deeper level. I read the white paper and began following the news and development in the space.
33 |
34 | Before writing this book, I wasn't actively involved in cryptocurrencies. While I love programming, ever since I started programming full-time, it's been difficult to find energy for side-projects, like I had during my University years.
35 |
36 | The free time I've had has been taken up by my two boys, my girlfriend and other hobbies. Saying no to something is saying yes to something else.◊mn{hobbies}
37 |
38 | ◊ndef["hobbies"]{
39 | My current hobbies include ◊link[bjj]{Brazilian Jiu-Jitsu}, boardgames, and standard low-effort ones such as reading books or watching videos.
40 |
41 | I have a very on-and-off relationship with my hobbies, which I pick up and do intensely for a short period of time. For instance, I've been into ◊link[lockpicking]{lockpicking}, ◊link[go]{Go}, and learning Korean, which I would like to start again some day.
42 |
43 | I have a ◊link[blog]{blog} where I write about random stuff, mostly for myself. It's also, as you might imagine, not updated regularly.
44 | }
45 |
46 | After my parental leave we decided that I should work part time to avoid long days at pre-school. Working less has also opened up time and energy for me to work on a side project, and writing a book has been on my bucket-list so I thought "why not do it now"? My girlfriend, wonderful as she is, has always been super supportive of my silly ideas.
47 |
48 | As the book project has been finishing up, I've started working on another cryptocurrency project: a ◊link[bitpal]{self-hosted payment processor}, that allows you to accept payments online without any third-party.
49 |
50 | Am I qualified to write about cryptocurrencies? Am I a cryptocurrency expert? I don't know. I just see myself as an enthusiast who is trying to write down and share my thoughts. Please keep that in mind when you consider any claims I make---I may very well miss something important.
51 |
52 | ◊(define blog
53 | (x-ref
54 | #f
55 | "http://www.jonashietala.se/"
56 | "My personal blog"))
57 | ◊(define bjj
58 | (x-ref
59 | "2019-04-12"
60 | "https://www.grapplearts.com/japanese-jiujitsu-vs-bjj/"
61 | "Stephan Kesting (2018) Japanese Jiujitsu vs BJJ"))
62 | ◊(define lockpicking
63 | (x-ref
64 | #f
65 | "https://www.reddit.com/r/lockpicking/"
66 | "Reddit: Lockpicking"))
67 | ◊(define go
68 | (x-ref
69 | "2019-04-12"
70 | "https://en.wikipedia.org/wiki/Go_(game)"
71 | "Wikipedia: Go (game)"))
72 | ◊(define bitpal
73 | (x-ref
74 | "2021-05-07"
75 | "https://bitpal.dev/"
76 | "BitPal"))
77 |
78 |
--------------------------------------------------------------------------------
/plots/data/WID_Data_Metadata/WID_Data_29102019-065949.csv:
--------------------------------------------------------------------------------
1 | Percentile;Year;"sptinc_992_j_US"
2 | p0p50;1913;
3 | p0p50;1914;
4 | p0p50;1915;
5 | p0p50;1916;
6 | p0p50;1917;
7 | p0p50;1918;
8 | p0p50;1919;
9 | p0p50;1920;
10 | p0p50;1921;
11 | p0p50;1922;
12 | p0p50;1923;
13 | p0p50;1924;
14 | p0p50;1925;
15 | p0p50;1926;
16 | p0p50;1927;
17 | p0p50;1928;
18 | p0p50;1929;
19 | p0p50;1930;
20 | p0p50;1931;
21 | p0p50;1932;
22 | p0p50;1933;
23 | p0p50;1934;
24 | p0p50;1935;
25 | p0p50;1936;
26 | p0p50;1937;
27 | p0p50;1938;
28 | p0p50;1939;
29 | p0p50;1940;
30 | p0p50;1941;
31 | p0p50;1942;
32 | p0p50;1943;
33 | p0p50;1944;
34 | p0p50;1945;
35 | p0p50;1946;
36 | p0p50;1947;
37 | p0p50;1948;
38 | p0p50;1949;
39 | p0p50;1950;
40 | p0p50;1951;
41 | p0p50;1952;
42 | p0p50;1953;
43 | p0p50;1954;
44 | p0p50;1955;
45 | p0p50;1956;
46 | p0p50;1957;
47 | p0p50;1958;
48 | p0p50;1959;
49 | p0p50;1960;
50 | p0p50;1961;
51 | p0p50;1962;0.19499999
52 | p0p50;1963;0.191
53 | p0p50;1964;0.18700001
54 | p0p50;1965;0.1913
55 | p0p50;1966;0.1956
56 | p0p50;1967;0.20829999
57 | p0p50;1968;0.20559999
58 | p0p50;1969;0.212
59 | p0p50;1970;0.20999999
60 | p0p50;1971;0.2035
61 | p0p50;1972;0.2013
62 | p0p50;1973;0.2027
63 | p0p50;1974;0.20730001
64 | p0p50;1975;0.2014
65 | p0p50;1976;0.20290001
66 | p0p50;1977;0.2
67 | p0p50;1978;0.199
68 | p0p50;1979;0.2008
69 | p0p50;1980;0.1989
70 | p0p50;1981;0.19509999
71 | p0p50;1982;0.18960001
72 | p0p50;1983;0.1831
73 | p0p50;1984;0.1788
74 | p0p50;1985;0.1788
75 | p0p50;1986;0.17659999
76 | p0p50;1987;0.1726
77 | p0p50;1988;0.16949999
78 | p0p50;1989;0.1693
79 | p0p50;1990;0.168
80 | p0p50;1991;0.1662
81 | p0p50;1992;0.1583
82 | p0p50;1993;0.15899999
83 | p0p50;1994;0.1578
84 | p0p50;1995;0.1538
85 | p0p50;1996;0.1508
86 | p0p50;1997;0.1486
87 | p0p50;1998;0.14910001
88 | p0p50;1999;0.1477
89 | p0p50;2000;0.1461
90 | p0p50;2001;0.1495
91 | p0p50;2002;0.14820001
92 | p0p50;2003;0.1452
93 | p0p50;2004;0.1419
94 | p0p50;2005;0.1384
95 | p0p50;2006;0.1354
96 | p0p50;2007;0.1374
97 | p0p50;2008;0.1371
98 | p0p50;2009;0.13590001
99 | p0p50;2010;0.1303
100 | p0p50;2011;0.12729999
101 | p0p50;2012;0.1238
102 | p0p50;2013;0.1277
103 | p0p50;2014;0.12549999
104 | p99p100;1913;0.1884
105 | p99p100;1914;0.19329999
106 | p99p100;1915;0.18700001
107 | p99p100;1916;0.20640001
108 | p99p100;1917;0.2014
109 | p99p100;1918;0.1895
110 | p99p100;1919;0.2101
111 | p99p100;1920;0.184
112 | p99p100;1921;0.18099999
113 | p99p100;1922;0.1763
114 | p99p100;1923;0.1689
115 | p99p100;1924;0.1761
116 | p99p100;1925;0.19949999
117 | p99p100;1926;0.2121
118 | p99p100;1927;0.2033
119 | p99p100;1928;0.2139
120 | p99p100;1929;0.21160001
121 | p99p100;1930;0.18089999
122 | p99p100;1931;0.1503
123 | p99p100;1932;0.1391
124 | p99p100;1933;0.1516
125 | p99p100;1934;0.1715
126 | p99p100;1935;0.1736
127 | p99p100;1936;0.19239999
128 | p99p100;1937;0.1904
129 | p99p100;1938;0.1719
130 | p99p100;1939;0.1848
131 | p99p100;1940;0.19310001
132 | p99p100;1941;0.19490001
133 | p99p100;1942;0.1849
134 | p99p100;1943;0.1718
135 | p99p100;1944;0.14839999
136 | p99p100;1945;0.1428
137 | p99p100;1946;0.1416
138 | p99p100;1947;0.14569999
139 | p99p100;1948;0.1577
140 | p99p100;1949;0.1517
141 | p99p100;1950;0.1585
142 | p99p100;1951;0.1494
143 | p99p100;1952;0.142
144 | p99p100;1953;0.13259999
145 | p99p100;1954;0.1349
146 | p99p100;1955;0.14129999
147 | p99p100;1956;0.1339
148 | p99p100;1957;0.13169999
149 | p99p100;1958;0.1247
150 | p99p100;1959;0.13070001
151 | p99p100;1960;0.1259
152 | p99p100;1961;0.1245
153 | p99p100;1962;0.1257
154 | p99p100;1963;
155 | p99p100;1964;0.1292
156 | p99p100;1965;
157 | p99p100;1966;0.12639999
158 | p99p100;1967;0.1214
159 | p99p100;1968;0.1242
160 | p99p100;1969;0.1151
161 | p99p100;1970;0.1079
162 | p99p100;1971;0.1108
163 | p99p100;1972;0.1112
164 | p99p100;1973;0.1102
165 | p99p100;1974;0.1055
166 | p99p100;1975;0.1058
167 | p99p100;1976;0.1041
168 | p99p100;1977;0.1075
169 | p99p100;1978;0.1063
170 | p99p100;1979;0.1115
171 | p99p100;1980;0.1067
172 | p99p100;1981;0.1105
173 | p99p100;1982;0.1126
174 | p99p100;1983;0.1151
175 | p99p100;1984;0.125
176 | p99p100;1985;0.12549999
177 | p99p100;1986;0.1221
178 | p99p100;1987;0.1331
179 | p99p100;1988;0.1488
180 | p99p100;1989;0.1446
181 | p99p100;1990;0.1454
182 | p99p100;1991;0.1389
183 | p99p100;1992;0.15009999
184 | p99p100;1993;0.1464
185 | p99p100;1994;0.1469
186 | p99p100;1995;0.15279999
187 | p99p100;1996;0.15970001
188 | p99p100;1997;0.1663
189 | p99p100;1998;0.1692
190 | p99p100;1999;0.1771
191 | p99p100;2000;0.18269999
192 | p99p100;2001;0.1727
193 | p99p100;2002;0.1706
194 | p99p100;2003;0.17200001
195 | p99p100;2004;0.1832
196 | p99p100;2005;0.1937
197 | p99p100;2006;0.20100001
198 | p99p100;2007;0.1987
199 | p99p100;2008;0.1952
200 | p99p100;2009;0.18539999
201 | p99p100;2010;0.198
202 | p99p100;2011;0.19599999
203 | p99p100;2012;0.2078
204 | p99p100;2013;0.19589999
205 | p99p100;2014;0.20200001
206 |
--------------------------------------------------------------------------------
/plots/data/WID_Data_Metadata/WID_Metadata_29102019-070448.csv:
--------------------------------------------------------------------------------
1 | Downloaded from wid.world on 29-10-2019 at 07:04:48
2 | Country Code;Country Name;WID Variable code;Percentile group;Unit;Variable name;Intuitive description;Technical description;Methodological Notes;Sources
3 | US;USA;shweal992j;p99p100;share;Net personal wealth. Share Adults. equal-split adults;Net personal wealth share held by a given percentile group. Net personal wealth is the total value of non-financial and financial assets (housing, land, deposits, bonds, equities, etc.) held by households, minus their debts. . The personal or household sector - in the national accounts sense - includes all households and private individuals (including those living in institutions), as well as unincorporated enterprises whose accounts are not separated from those of the households who own them. The population is comprised of individuals over age 20. The base unit is the individual (rather than the household) but resources are split equally within couples.;[Net personal wealth]=[Personal non-financial assets]+[Personal financial assets]-[Personal debt];WID.world computations using wid.world/gpinter.;"[URL][URL_LINK]http://wid.world/document/t-piketty-e-saez-g-zucman-data-appendix-to-distributional-national-accounts-methods-and-estimates-for-the-united-states-2016/[/URL_LINK][URL_TEXT]Piketty, Thomas; Saez, Emmanuel and Zucman, Gabriel (2016). Distributional National Accounts: Methods and Estimates for the United States.[/URL_TEXT][/URL]"
4 | US;USA;shweal992i;p99p100;share;Net personal wealth. Share Adults. individuals;Net personal wealth share held by a given percentile group. Net personal wealth is the total value of non-financial and financial assets (housing, land, deposits, bonds, equities, etc.) held by households, minus their debts. . The personal or household sector - in the national accounts sense - includes all households and private individuals (including those living in institutions), as well as unincorporated enterprises whose accounts are not separated from those of the households who own them. The population is comprised of individuals over age 20. The base unit is the individual (rather than the household). This is equivalent to assuming no sharing of resources within couples.;[Net personal wealth]=[Personal non-financial assets]+[Personal financial assets]-[Personal debt];WID.world computations using wid.world/gpinter.;"[URL][URL_LINK]http://wid.world/document/t-piketty-e-saez-g-zucman-data-appendix-to-distributional-national-accounts-methods-and-estimates-for-the-united-states-2016/[/URL_LINK][URL_TEXT]Piketty, Thomas; Saez, Emmanuel and Zucman, Gabriel (2016). Distributional National Accounts: Methods and Estimates for the United States.[/URL_TEXT][/URL]"
5 | US;USA;shweal992j;p50p90;share;Net personal wealth. Share Adults. equal-split adults;Net personal wealth share held by a given percentile group. Net personal wealth is the total value of non-financial and financial assets (housing, land, deposits, bonds, equities, etc.) held by households, minus their debts. . The personal or household sector - in the national accounts sense - includes all households and private individuals (including those living in institutions), as well as unincorporated enterprises whose accounts are not separated from those of the households who own them. The population is comprised of individuals over age 20. The base unit is the individual (rather than the household) but resources are split equally within couples.;[Net personal wealth]=[Personal non-financial assets]+[Personal financial assets]-[Personal debt];WID.world computations using wid.world/gpinter.;"[URL][URL_LINK]http://wid.world/document/t-piketty-e-saez-g-zucman-data-appendix-to-distributional-national-accounts-methods-and-estimates-for-the-united-states-2016/[/URL_LINK][URL_TEXT]Piketty, Thomas; Saez, Emmanuel and Zucman, Gabriel (2016). Distributional National Accounts: Methods and Estimates for the United States.[/URL_TEXT][/URL]"
6 | US;USA;shweal992i;p50p90;share;Net personal wealth. Share Adults. individuals;Net personal wealth share held by a given percentile group. Net personal wealth is the total value of non-financial and financial assets (housing, land, deposits, bonds, equities, etc.) held by households, minus their debts. . The personal or household sector - in the national accounts sense - includes all households and private individuals (including those living in institutions), as well as unincorporated enterprises whose accounts are not separated from those of the households who own them. The population is comprised of individuals over age 20. The base unit is the individual (rather than the household). This is equivalent to assuming no sharing of resources within couples.;[Net personal wealth]=[Personal non-financial assets]+[Personal financial assets]-[Personal debt];WID.world computations using wid.world/gpinter.;"[URL][URL_LINK]http://wid.world/document/t-piketty-e-saez-g-zucman-data-appendix-to-distributional-national-accounts-methods-and-estimates-for-the-united-states-2016/[/URL_LINK][URL_TEXT]Piketty, Thomas; Saez, Emmanuel and Zucman, Gabriel (2016). Distributional National Accounts: Methods and Estimates for the United States.[/URL_TEXT][/URL]"
7 |
--------------------------------------------------------------------------------
/plots/data/WID_Data_Metadata/WID_Data_29102019-070448.csv:
--------------------------------------------------------------------------------
1 | Percentile;Year;"shweal_z_US"
2 | p99p100;1913;0.45120001
3 | p99p100;1914;0.44589999
4 | p99p100;1915;0.44639999
5 | p99p100;1916;0.433
6 | p99p100;1917;0.40529999
7 | p99p100;1918;0.3705
8 | p99p100;1919;0.40009999
9 | p99p100;1920;0.3567
10 | p99p100;1921;0.368
11 | p99p100;1922;0.39969999
12 | p99p100;1923;0.3538
13 | p99p100;1924;0.37459999
14 | p99p100;1925;0.40920001
15 | p99p100;1926;0.4258
16 | p99p100;1927;0.4492
17 | p99p100;1928;0.47819999
18 | p99p100;1929;0.4799
19 | p99p100;1930;0.4337
20 | p99p100;1931;0.38620001
21 | p99p100;1932;0.38100001
22 | p99p100;1933;0.4034
23 | p99p100;1934;0.40990001
24 | p99p100;1935;0.4048
25 | p99p100;1936;0.42989999
26 | p99p100;1937;0.4366
27 | p99p100;1938;0.3976
28 | p99p100;1939;0.4082
29 | p99p100;1940;0.37670001
30 | p99p100;1941;0.3457
31 | p99p100;1942;0.34110001
32 | p99p100;1943;0.34380001
33 | p99p100;1944;0.3184
34 | p99p100;1945;0.32100001
35 | p99p100;1946;0.29929999
36 | p99p100;1947;0.2868
37 | p99p100;1948;0.2807
38 | p99p100;1949;0.2719
39 | p99p100;1950;0.28529999
40 | p99p100;1951;0.28099999
41 | p99p100;1952;0.27770001
42 | p99p100;1953;0.26539999
43 | p99p100;1954;0.27239999
44 | p99p100;1955;0.27540001
45 | p99p100;1956;0.27900001
46 | p99p100;1957;0.2753
47 | p99p100;1958;0.2712
48 | p99p100;1959;0.27770001
49 | p99p100;1960;0.27770001
50 | p99p100;1961;0.27959999
51 | p99p100;1962;0.28099999
52 | p99p100;1963;0.2762
53 | p99p100;1964;0.27129999
54 | p99p100;1965;0.2687
55 | p99p100;1966;0.29010001
56 | p99p100;1967;0.28650001
57 | p99p100;1968;0.30500001
58 | p99p100;1969;0.28929999
59 | p99p100;1970;0.2809
60 | p99p100;1971;0.25420001
61 | p99p100;1972;0.2472
62 | p99p100;1973;0.25940001
63 | p99p100;1974;0.2457
64 | p99p100;1975;0.228
65 | p99p100;1976;0.22139999
66 | p99p100;1977;0.23119999
67 | p99p100;1978;0.21960001
68 | p99p100;1979;0.22400001
69 | p99p100;1980;0.2254
70 | p99p100;1981;0.24420001
71 | p99p100;1982;0.24690001
72 | p99p100;1983;0.2374
73 | p99p100;1984;0.2386
74 | p99p100;1985;0.2314
75 | p99p100;1986;0.2298
76 | p99p100;1987;0.24609999
77 | p99p100;1988;0.26499999
78 | p99p100;1989;0.26570001
79 | p99p100;1990;0.27430001
80 | p99p100;1991;0.26879999
81 | p99p100;1992;0.2757
82 | p99p100;1993;0.28830001
83 | p99p100;1994;0.28839999
84 | p99p100;1995;0.27919999
85 | p99p100;1996;0.28580001
86 | p99p100;1997;0.29460001
87 | p99p100;1998;0.32609999
88 | p99p100;1999;0.31470001
89 | p99p100;2000;0.32300001
90 | p99p100;2001;0.3337
91 | p99p100;2002;0.32269999
92 | p99p100;2003;0.30320001
93 | p99p100;2004;0.3317
94 | p99p100;2005;0.32100001
95 | p99p100;2006;0.3283
96 | p99p100;2007;0.35100001
97 | p99p100;2008;0.36090001
98 | p99p100;2009;0.36149999
99 | p99p100;2010;0.38999999
100 | p99p100;2011;0.3743
101 | p99p100;2012;0.38850001
102 | p99p100;2013;0.38280001
103 | p99p100;2014;0.3856
104 | p50p90;1913;
105 | p50p90;1914;
106 | p50p90;1915;
107 | p50p90;1916;
108 | p50p90;1917;
109 | p50p90;1918;
110 | p50p90;1919;
111 | p50p90;1920;
112 | p50p90;1921;
113 | p50p90;1922;
114 | p50p90;1923;
115 | p50p90;1924;
116 | p50p90;1925;
117 | p50p90;1926;
118 | p50p90;1927;
119 | p50p90;1928;
120 | p50p90;1929;
121 | p50p90;1930;
122 | p50p90;1931;
123 | p50p90;1932;
124 | p50p90;1933;
125 | p50p90;1934;
126 | p50p90;1935;
127 | p50p90;1936;
128 | p50p90;1937;
129 | p50p90;1938;
130 | p50p90;1939;
131 | p50p90;1940;
132 | p50p90;1941;
133 | p50p90;1942;
134 | p50p90;1943;
135 | p50p90;1944;
136 | p50p90;1945;
137 | p50p90;1946;
138 | p50p90;1947;
139 | p50p90;1948;
140 | p50p90;1949;
141 | p50p90;1950;
142 | p50p90;1951;
143 | p50p90;1952;
144 | p50p90;1953;
145 | p50p90;1954;
146 | p50p90;1955;
147 | p50p90;1956;
148 | p50p90;1957;
149 | p50p90;1958;
150 | p50p90;1959;
151 | p50p90;1960;
152 | p50p90;1961;
153 | p50p90;1962;0.26730001
154 | p50p90;1963;0.27970001
155 | p50p90;1964;0.2879
156 | p50p90;1965;0.2872
157 | p50p90;1966;0.29809999
158 | p50p90;1967;0.30669999
159 | p50p90;1968;0.2762
160 | p50p90;1969;0.31220001
161 | p50p90;1970;0.30860001
162 | p50p90;1971;0.31529999
163 | p50p90;1972;0.31389999
164 | p50p90;1973;0.30070001
165 | p50p90;1974;0.33399999
166 | p50p90;1975;0.31760001
167 | p50p90;1976;0.3405
168 | p50p90;1977;0.3434
169 | p50p90;1978;0.35339999
170 | p50p90;1979;0.33700001
171 | p50p90;1980;0.33840001
172 | p50p90;1981;0.3486
173 | p50p90;1982;0.35499999
174 | p50p90;1983;0.36219999
175 | p50p90;1984;0.36629999
176 | p50p90;1985;0.3732
177 | p50p90;1986;0.36449999
178 | p50p90;1987;0.35409999
179 | p50p90;1988;0.34959999
180 | p50p90;1989;0.3493
181 | p50p90;1990;0.34259999
182 | p50p90;1991;0.34349999
183 | p50p90;1992;0.33849999
184 | p50p90;1993;0.32749999
185 | p50p90;1994;0.32879999
186 | p50p90;1995;0.32730001
187 | p50p90;1996;0.32350001
188 | p50p90;1997;0.33019999
189 | p50p90;1998;0.31040001
190 | p50p90;1999;0.30309999
191 | p50p90;2000;0.3017
192 | p50p90;2001;0.3258
193 | p50p90;2002;0.31330001
194 | p50p90;2003;0.32710001
195 | p50p90;2004;0.30610001
196 | p50p90;2005;0.3064
197 | p50p90;2006;0.31380001
198 | p50p90;2007;0.3008
199 | p50p90;2008;0.28470001
200 | p50p90;2009;0.292
201 | p50p90;2010;0.2744
202 | p50p90;2011;0.27309999
203 | p50p90;2012;0.2658
204 | p50p90;2013;0.28169999
205 | p50p90;2014;0.27149999
206 |
--------------------------------------------------------------------------------
/plots/data/usa-debt.csv:
--------------------------------------------------------------------------------
1 | DATE,GFDEBTN
2 | 1966-01-01,320999
3 | 1966-04-01,316097
4 | 1966-07-01,324748
5 | 1966-10-01,329319
6 | 1967-01-01,330947
7 | 1967-04-01,322893
8 | 1967-07-01,335896
9 | 1967-10-01,344663
10 | 1968-01-01,349473
11 | 1968-04-01,345369
12 | 1968-07-01,354743
13 | 1968-10-01,358029
14 | 1969-01-01,359546
15 | 1969-04-01,352895
16 | 1969-07-01,360685
17 | 1969-10-01,368226
18 | 1970-01-01,372007
19 | 1970-04-01,370094
20 | 1970-07-01,378678
21 | 1970-10-01,389158
22 | 1971-01-01,391668
23 | 1971-04-01,397305
24 | 1971-07-01,412268
25 | 1971-10-01,424131
26 | 1972-01-01,427344
27 | 1972-04-01,426435
28 | 1972-07-01,433946
29 | 1972-10-01,448473
30 | 1973-01-01,458606
31 | 1973-04-01,457317
32 | 1973-07-01,460614
33 | 1973-10-01,469073
34 | 1974-01-01,473675
35 | 1974-04-01,474235
36 | 1974-07-01,481466
37 | 1974-10-01,492664
38 | 1975-01-01,509659
39 | 1975-04-01,533188
40 | 1975-07-01,553647
41 | 1975-10-01,576649
42 | 1976-01-01,600490
43 | 1976-04-01,620432
44 | 1976-07-01,634701
45 | 1976-10-01,653543
46 | 1977-01-01,669207
47 | 1977-04-01,674425
48 | 1977-07-01,698840
49 | 1977-10-01,718943
50 | 1978-01-01,737951
51 | 1978-04-01,749024
52 | 1978-07-01,771544
53 | 1978-10-01,789207
54 | 1979-01-01,796792
55 | 1979-04-01,804913
56 | 1979-07-01,826519
57 | 1979-10-01,845116
58 | 1980-01-01,863451
59 | 1980-04-01,877614
60 | 1980-07-01,907701
61 | 1980-10-01,930210
62 | 1981-01-01,964531
63 | 1981-04-01,971174
64 | 1981-07-01,997855
65 | 1981-10-01,1028729
66 | 1982-01-01,1061299
67 | 1982-04-01,1079630
68 | 1982-07-01,1142035
69 | 1982-10-01,1197074
70 | 1983-01-01,1244493
71 | 1983-04-01,1319581
72 | 1983-07-01,1377211
73 | 1983-10-01,1410702
74 | 1984-01-01,1463741
75 | 1984-04-01,1512697
76 | 1984-07-01,1572267
77 | 1984-10-01,1662966
78 | 1985-01-01,1710731
79 | 1985-04-01,1774640
80 | 1985-07-01,1823103
81 | 1985-10-01,1945942
82 | 1986-01-01,1986816
83 | 1986-04-01,2059349
84 | 1986-07-01,2125304
85 | 1986-10-01,2214835
86 | 1987-01-01,2246724
87 | 1987-04-01,2309296
88 | 1987-07-01,2350277
89 | 1987-10-01,2431715
90 | 1988-01-01,2487551
91 | 1988-04-01,2547656
92 | 1988-07-01,2602183
93 | 1988-10-01,2684392
94 | 1989-01-01,2740898
95 | 1989-04-01,2799923
96 | 1989-07-01,2857431
97 | 1989-10-01,2952994
98 | 1990-01-01,3051958
99 | 1990-04-01,3143754
100 | 1990-07-01,3233313
101 | 1990-10-01,3364820
102 | 1991-01-01,3465189
103 | 1991-04-01,3537988
104 | 1991-07-01,3665303
105 | 1991-10-01,3801698
106 | 1992-01-01,3881288
107 | 1992-04-01,3984656
108 | 1992-07-01,4064621
109 | 1992-10-01,4177009
110 | 1993-01-01,4230580
111 | 1993-04-01,4351950
112 | 1993-07-01,4411489
113 | 1993-10-01,4535687
114 | 1994-01-01,4575869
115 | 1994-04-01,4645802
116 | 1994-07-01,4692750
117 | 1994-10-01,4800150
118 | 1995-01-01,4864116
119 | 1995-04-01,4951372
120 | 1995-07-01,4973983
121 | 1995-10-01,4988665
122 | 1996-01-01,5117786
123 | 1996-04-01,5161076
124 | 1996-07-01,5224811
125 | 1996-10-01,5323172
126 | 1997-01-01,5380890
127 | 1997-04-01,5376151
128 | 1997-07-01,5413146
129 | 1997-10-01,5502388
130 | 1998-01-01,5542426
131 | 1998-04-01,5547935
132 | 1998-07-01,5526193
133 | 1998-10-01,5614217
134 | 1999-01-01,5651615
135 | 1999-04-01,5638780
136 | 1999-07-01,5656271
137 | 1999-10-01,5776091
138 | 2000-01-01,5773392
139 | 2000-04-01,5685938
140 | 2000-07-01,5674179
141 | 2000-10-01,5662216
142 | 2001-01-01,5773740
143 | 2001-04-01,5726815
144 | 2001-07-01,5807464
145 | 2001-10-01,5943439
146 | 2002-01-01,6006032
147 | 2002-04-01,6126469
148 | 2002-07-01,6228236
149 | 2002-10-01,6405707
150 | 2003-01-01,6460776
151 | 2003-04-01,6670121
152 | 2003-07-01,6783320
153 | 2003-10-01,6997964
154 | 2004-01-01,7131068
155 | 2004-04-01,7274335
156 | 2004-07-01,7379053
157 | 2004-10-01,7596143
158 | 2005-01-01,7776939
159 | 2005-04-01,7836496
160 | 2005-07-01,7932710
161 | 2005-10-01,8170413
162 | 2006-01-01,8371156
163 | 2006-04-01,8420042
164 | 2006-07-01,8506974
165 | 2006-10-01,8680224
166 | 2007-01-01,8849665
167 | 2007-04-01,8867677
168 | 2007-07-01,9007653
169 | 2007-10-01,9229172
170 | 2008-01-01,9437594
171 | 2008-04-01,9492006
172 | 2008-07-01,10024725
173 | 2008-10-01,10699805
174 | 2009-01-01,11126941
175 | 2009-04-01,11545275
176 | 2009-07-01,11909828
177 | 2009-10-01,12311349
178 | 2010-01-01,12773123
179 | 2010-04-01,13201792
180 | 2010-07-01,13561622
181 | 2010-10-01,14025215
182 | 2011-01-01,14270114
183 | 2011-04-01,14343087
184 | 2011-07-01,14790340
185 | 2011-10-01,15222940
186 | 2012-01-01,15606518
187 | 2012-04-01,15855037
188 | 2012-07-01,16066240
189 | 2012-10-01,16432730
190 | 2013-01-01,16771381
191 | 2013-04-01,16738320
192 | 2013-07-01,16738180
193 | 2013-10-01,17156119
194 | 2014-01-01,17601227
195 | 2014-04-01,17632606
196 | 2014-07-01,17824071
197 | 2014-10-01,18141444
198 | 2015-01-01,18152056
199 | 2015-04-01,18151998
200 | 2015-07-01,18150618
201 | 2015-10-01,18922179
202 | 2016-01-01,19264939
203 | 2016-04-01,19381591
204 | 2016-07-01,19573445
205 | 2016-10-01,19976827
206 | 2017-01-01,19846420
207 | 2017-04-01,19844554
208 | 2017-07-01,20244900
209 | 2017-10-01,20492747
210 | 2018-01-01,21089643
211 | 2018-04-01,21195070.0
212 | 2018-07-01,21516058.0
213 | 2018-10-01,21974096.0
214 | 2019-01-01,22027880.0
215 | 2019-04-01,22023283.0
216 | 2019-07-01,22719402.0
217 | 2019-10-01,23201380.0
218 | 2020-01-01,23223813.0
219 | 2020-04-01,26477241.0
220 | 2020-07-01,26945391.0
221 |
--------------------------------------------------------------------------------
/plots/income-inequality-world.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import numpy as np
3 | import math
4 | from matplotlib.ticker import FuncFormatter
5 |
6 | from matplotlib import patheffects
7 | import matplotlib as mpl
8 | import csv
9 | import datetime as dt
10 |
11 | import matplotlib.patches as mpatches
12 | import matplotlib.dates as mdates
13 |
14 | def parse(url):
15 | with open(url, 'r') as infile:
16 | reader = csv.DictReader(infile, delimiter=';')
17 |
18 | europe = []
19 | europe_year = []
20 | china = []
21 | china_year = []
22 | usa = []
23 | usa_year = []
24 | india = []
25 | india_year = []
26 | russia = []
27 | russia_year = []
28 | swe = []
29 | swe_year = []
30 |
31 | europe_id = 'sptinc_z_QE'
32 | china_id = 'sptinc_z_CN'
33 | usa_id = 'sptinc_z_US'
34 | india_id = 'sptinc_z_IN'
35 | russia_id = 'sptinc_z_RU'
36 | swe_id = 'sptinc_z_SE'
37 |
38 | for row in reader:
39 | #print(row)
40 | year = int(row['Year'])
41 | if row[europe_id]:
42 | europe.append(float(row[europe_id]))
43 | europe_year.append(year)
44 | if row[china_id]:
45 | china.append(float(row[china_id]))
46 | china_year.append(year)
47 | if row[usa_id]:
48 | usa.append(float(row[usa_id]))
49 | usa_year.append(year)
50 | if row[india_id]:
51 | india.append(float(row[india_id]))
52 | india_year.append(year)
53 | if row[russia_id]:
54 | russia.append(float(row[russia_id]))
55 | russia_year.append(year)
56 | if row[swe_id]:
57 | swe.append(float(row[swe_id]))
58 | swe_year.append(year)
59 | return (europe, europe_year,
60 | china, china_year,
61 | usa, usa_year,
62 | india, india_year,
63 | russia, russia_year,
64 | swe, swe_year)
65 |
66 |
67 | # https://wid.world/
68 | (europe, europe_year,
69 | china, china_year,
70 | usa, usa_year,
71 | india, india_year,
72 | russia, russia_year,
73 | swe, swe_year) = parse('data/WID_Data_Metadata/WID_Data_29102019-071549.csv')
74 |
75 | # It's just for the high level understanding. Plus xkcd style is pretty
76 | plt.xkcd()
77 |
78 | mycol = '#343535'
79 | background_col = '#fcfcfc'
80 |
81 | plt.rcParams["font.family"] = "Concourse T4"
82 | plt.rcParams["axes.linewidth"] = 1
83 | plt.rcParams["axes.edgecolor"] = mycol
84 | plt.rcParams["text.color"] = mycol
85 | plt.rcParams["xtick.color"] = mycol
86 | plt.rcParams["ytick.color"] = mycol
87 | plt.rcParams["ytick.minor.width"] = 0
88 | plt.rcParams['figure.facecolor'] = background_col
89 |
90 | # Removes remaining white lines after xkcdifying the plot.
91 | # Changing the background didn't fix it.
92 | mpl.rcParams['path.effects'] = [patheffects.withStroke(linewidth=0)]
93 |
94 | fig = plt.figure(figsize=(8, 5))
95 | ax = fig.add_subplot(1, 1, 1)
96 | fig.set_facecolor(background_col)
97 | ax.set_facecolor(background_col)
98 |
99 | def y_fmt(y, pos):
100 | return ' {:,.0f}%'.format(y*100)
101 | ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
102 | # ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
103 |
104 | # def y_fmt(y, pos):
105 | # return '${:,.0f}'.format(int(y/1000000))
106 | # ax.get_yaxis().set_major_formatter(FuncFormatter(y_fmt))
107 | #ax.get_xaxis().set_major_formatter(mdates.DateFormatter('%Y'))
108 | #ax.set_ylabel("Debt in trillions")
109 |
110 | ax.spines['right'].set_color('none')
111 | ax.spines['top'].set_color('none')
112 |
113 | ax.xaxis.set_tick_params(width=2)
114 | ax.yaxis.set_tick_params(width=2)
115 |
116 | # ax.set_xticks([dt.date(y, 1, 1) for y in [1970, 1980, 1990, 2000, 2010, 2018]])
117 | #ax.set_yticks((0, 25, 50, 75, 100, 125))
118 | #ax.set_ylim(0.1, 0.25)
119 | ax.set_xlim(1980, 2015)
120 | #ax.set_xticks((1962, 1970, 1980, 1990, 2000, 2010))
121 | #ax.set_xlim(dt.datetime(1965, 01, 01), dt.datetime(2019, 01, 31))
122 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1966, 1970, 1975, 1980, 1985, 1990, 1995, 2000, 2005, 2010, 2015, 2019]])
123 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1966, 1980, 1990, 2000, 2010, 2019]])
124 | #ax.set_xticks([dt.datetime(y, 01, 01) for y in [1970, 1980, 1990, 2000, 2010, 2019]])
125 | #ax.set_ylim(0, 25000000)
126 | #ax.set_yticks((0, 20, 40, 60, 80, 100))
127 |
128 | # #396AB1
129 | # #DA7C30
130 | # #3E9651
131 | # #CC2529
132 | # #535154
133 | # #6B4C9A
134 | # #922428
135 | # #948B3D
136 |
137 | # For different y-axis:
138 | # https://stackoverflow.com/questions/9103166/multiple-axis-in-matplotlib-with-different-scales
139 | ax.plot(india_year, india, '#DA7C30', label='India')
140 | ax.plot(usa_year, usa, '#396AB1', label='USA')
141 | ax.plot(russia_year, russia, '#CC2529', label='Russia')
142 | ax.plot(china_year, china, '#3E9651', label='China')
143 | ax.plot(europe_year, europe, '#535154', label='Europe')
144 | ax.plot(swe_year, swe, '#948B3D', label='Sweden')
145 | #ax.plot(bottom50_year, bottom50, '#CC2529', label='Bottom 50%')
146 |
147 | ax.legend(loc='best')
148 |
149 | plt.savefig('income-inequality-world.svg', format="svg", transparent=False, bbox_inches='tight')
150 | print("done")
151 |
152 |
--------------------------------------------------------------------------------
/chapter.html.p:
--------------------------------------------------------------------------------
1 | ◊(local-require pollen/tag)
2 | ◊(define title (select-from-metas 'title here))
3 | ◊(define head-title
4 | (if title
5 | (string-append main-title ": " title)
6 | (error (format "unknown title for ~v~n" here))))
7 | ◊(define (str->date-display text x)
8 | (if x
9 | (string-append text " " (~t (iso8601->date x) "MMMM d, y"))
10 | ""))
11 | ◊(define subtitle (select-from-metas 'subtitle here))
12 | ◊(define published-date (select-from-metas 'published here))
13 | ◊(define published (str->date-display "Published" published-date))
14 | ◊(define updated-date (select-from-metas 'updated here))
15 | ◊(define updated
16 | (if (or (not updated-date) (string=? published-date updated-date))
17 | #f
18 | (str->date-display "Updated" (select-from-metas 'updated here))))
19 | ◊(define side-space? (not (select-from-metas 'no-side-space here)))
20 | ◊(define section-chapters-headers? (not (select-from-metas 'no-section-chapters-header here)))
21 | ◊(define article-class
22 | (let ((extra (select-from-metas 'extra-article-class here)))
23 | (if extra
24 | (string-append "chapter " extra)
25 | "chapter")))
26 | ◊(define prev-page
27 | (let ((p (previous here)))
28 | (if (equal? p 'index.html)
29 | #f
30 | p)))
31 | ◊(define prev-title
32 | (if prev-page
33 | (select-from-metas 'title prev-page)
34 | #f))
35 | ◊(define next-page (next here))
36 | ◊(define next-title
37 | (if next-page
38 | (select-from-metas 'title next-page)
39 | #f))
40 | ◊(define parent-page (parent here))
41 | ◊(define parent-title
42 | (if parent-page
43 | (select-from-metas 'title parent-page)
44 | #f))
45 | ◊(define (ref page title txt)
46 | (->html
47 | (make-link #:title title (string-append "/" (symbol->string page)) txt)))
48 |
49 |
50 |
51 |
52 | ◊|head-title|
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
67 |
68 |