@sdelsad/commodity-desk-daily 1.0.17 → 1.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/ep04.md ADDED
@@ -0,0 +1,192 @@
1
+ # Soft Commodity Trading — Ep 4
2
+ ## The Physical Chain, End to End
3
+
4
+ ---
5
+
6
+ ## Market pulse
7
+
8
+ **The August WASDE was friendly — and the Black Sea turned violent again the same night.**
9
+
10
+ | Contract | Close (Wed) | Change |
11
+ |---|---|---|
12
+ | Corn, December | $4.80¾ /bu | +20¼¢ (two-week high) |
13
+ | Soybeans, November | $11.83¼ /bu | +14½¢ |
14
+ | Wheat, Chicago September | $6.52¾ /bu | +22½¢ |
15
+ | Wheat, Kansas City September | $7.20¾ /bu | +21½¢ |
16
+
17
+ USDA cut its corn yield to **180.7 bu/acre** — below the 182.5 average trade guess and near the bottom of the 180–185 range of estimates. New-crop corn ending stocks fell from 1.79 to **1.653 billion bushels**. Soybeans were the odd one out: production came in 44 million bushels *above* July and 41 above the trade — a bearish supply line — yet November beans closed higher, because USDA raised crush by 30 million bushels and corn pulled the whole floor up. The lesson of the day's tape: the market trades the whole balance sheet, not one row of it.
18
+
19
+ ```chart
20
+ {"type":"line","mode":"index","unit":"Mon 10 Aug = 100","title":"WASDE week on the board",
21
+ "caption":"Two sessions of defensive drift, one report: corn jumped to a two-week high and wheat ended above where Monday left it.",
22
+ "source":"CME settlements 10-12 Aug 2026 (episode pulses; Pro Farmer)",
23
+ "x":["Mon 10","Tue 11","Wed 12"],
24
+ "series":[{"name":"Dec corn","values":[465,460.5,480.75]},
25
+ {"name":"Nov soybeans","values":[1182,1168.75,1183.25]},
26
+ {"name":"Sep Chicago wheat","values":[651,630.25,652.75]}]}
27
+ ```
28
+
29
+ **The geopolitical read.** Tuesday's rumour of safe-passage talks died overnight: Ukrainian drones struck Novorossiysk and idled its two big grain terminals — the Demetra-controlled Novorossiysk Grain Terminal (~8.5 Mt/yr) and the NKHP terminal (~7.1 Mt/yr), together more than 15.5 Mt of annual export capacity. On Tuesday the market priced trapped grain getting *out*; on Wednesday it priced Russian loading capacity going *dark* — the same transmission mechanism, running in reverse. Russia's August wheat exports were already estimated at only 3.0–3.4 Mt. Turkey is floating a plan for two protected corridors, one along each coast; nothing is signed. And every vessel anchored off Novorossiysk is now on a clock that is denominated in dollars per day — which is today's subject.
30
+
31
+ ---
32
+
33
+ ### Key takeaways
34
+
35
+ - **Incoterms** are the trade's risk-allocation vocabulary. **FOB**: seller delivers over the ship's rail; buyer charters, insures, owns the voyage. **CFR**: seller also pays the freight. **CIF**: seller pays freight *and* insurance.
36
+ - The trap: in all three, **risk passes at the load port**. On a CIF cargo the seller pays freight to destination and buys the insurance — yet the voyage runs at the *buyer's* risk, and the buyer claims on the policy the seller bought. **Cost and risk travel separately.**
37
+ - Execution is a clock: **laycan** (loading window) → **nomination** of the vessel → **notice of readiness** (NOR starts the clock) → **laytime** (allowed loading time, counted in weather working days) → **demurrage** if exceeded, **despatch** (customarily half the demurrage rate) if beaten.
38
+ - Worked example: 60,000 t FOB Santos, laytime 6 days at 10,000 t/day, port queue makes loading take 9. Demurrage $24,000/day × 3 = **$72,000** — 11% of the $660k margin — while financing the $26M cargo costs another ~$4,300/day. Nothing moved on the screen.
39
+ - Demurrage claims cascade through the contract string and are fought on the **statement of facts**. Missing a **laycan** lets the counterparty cancel — a one-day slip can put the entire flat-price move on your book.
40
+ - At loading the cargo becomes paper: **draft survey** for weight, load-port **quality certificate that is final**, and the **bill of lading** — receipt, contract of carriage and document of title in one. Backdating a B/L is fraud, and it has sunk trading houses.
41
+ - Execution desks are a **profit centre**: they win claims, earn despatch, and save the days traders give away.
42
+
43
+ ### Vocabulary of the day
44
+
45
+ | Term | Meaning |
46
+ |---|---|
47
+ | Incoterms | Standard three-letter trade terms allocating cost and risk between buyer and seller |
48
+ | FOB / CFR / CIF | Free on board / cost and freight / cost, insurance and freight — risk passes at loading in all three |
49
+ | Charter party | The contract hiring the vessel, between charterer and shipowner |
50
+ | Nomination | Formally naming the performing vessel under a cargo contract |
51
+ | Notice of readiness (NOR) | The master's declaration that the vessel has arrived and is ready — starts laytime |
52
+ | Laytime | The contractually allowed time to load or discharge before demurrage begins |
53
+ | Weather working day | A laytime day that counts only when weather permits cargo work |
54
+ | Despatch | Reward for loading faster than laytime, customarily half the demurrage rate |
55
+ | Statement of facts | The port log of events both sides use to fight laytime claims |
56
+ | Draft survey | Weighing the cargo by the ship's displacement, before and after loading |
57
+ | Bill of lading (B/L) | Receipt, contract of carriage and document of title in one — holder owns the cargo |
58
+ | Cancelling date | The last day of the laycan, after which the counterparty may cancel |
59
+
60
+ ---
61
+
62
+ ## Quiz — Day 4
63
+
64
+ **J-0 — Episode 4: The physical chain, end to end**
65
+
66
+ **Q1.** You sold 60,000 t of soybeans CIF Qingdao. Mid-ocean, the vessel takes on water and the cargo is ruined. The buyer emails: "Your ship, your freight, your insurance — send a replacement cargo." Are they right? Who bears the loss, who claims on the insurance, and what exactly does the buyer still have to do under the contract?
67
+
68
+ **Q2.** A Supramax loads 48,000 t of corn at a rate of 8,000 t per weather working day. She tenders NOR on the 3rd; loading actually takes 10 calendar days, but the statement of facts shows 2 full days of rain during which no work was possible. Demurrage is $18,500/day, despatch half. Who owes whom, and how much? What single document decides the argument?
69
+
70
+ **Q3.** You are the FOB seller. Laycan is 15–25 November; on the 24th your cargo is still 20,000 t short because your up-country supplier defaulted. The buyer's vessel has been at anchor since the 18th, and December futures have rallied 40¢ since you signed. Describe your three exposures, in dollars where possible (cargo 60,000 t ≈ 2.2 M bu, demurrage $24,000/day), and rank them.
71
+
72
+ **J-1 — Episode 3: Futures plumbing and the shape of the curve**
73
+
74
+ **Q4.** Your desk is short 80 September wheat lots hedging inventory, and needs the hedge in December. The broker quotes "Sep-Dec fifteen, Dec over." When you roll, do you pay the fifteen cents or receive it? And what does that answer tell you about what a carry market does to the economics of hedged storage?
75
+
76
+ **Q5.** Wednesday's WASDE moved December corn up 20¼¢. A merchant was short 100 lots as a hedge against bought physical. (a) How much variation margin left the account, and when? (b) The physical gained roughly the same — so why does the CFO still care? (c) Name the ep-3 rule this illustrates.
77
+
78
+ **J-3 — Episode 1: The units and the language of the desk**
79
+
80
+ **Q6.** A colleague says: "We're long fifty lots of Matif wheat and short fifty lots of Chicago wheat — flat, more or less." How many tonnes is each leg? Is the book flat? Give the sizes and name every mismatch you can see.
81
+
82
+ **Q7.** Decode this broker line word by word: "He's bid four eighty for fifty December, offered at four eighty and a half — the half's workable." What is being bought and sold, what size, at what prices, and what does "workable" change?
83
+
84
+ ---
85
+
86
+ <br><br>
87
+
88
+ ## ▼ SOLUTIONS (spoilers) — scroll only after answering ▼
89
+
90
+ <br><br>
91
+
92
+ **A1.** The buyer is wrong. Under **CIF, risk passes at the load port** — the seller's obligations are to ship a conforming cargo, pay freight to Qingdao, procure insurance for the buyer's benefit, and tender clean documents. The mid-ocean loss is the **buyer's risk**; the **buyer claims on the policy the seller bought** (the policy is assigned with the documents). And the sting: CIF is a *documents* trade — if the seller tenders a clean bill of lading, load-port quality certificate and insurance policy, the buyer must **pay against documents in full**, then recover from underwriters. No replacement cargo is owed. The trap: assuming that whoever pays for the voyage carries its risk — cost and risk travel separately.
93
+
94
+ **A2.** Allowed laytime = 48,000 ÷ 8,000 = **6 weather working days**. Ten calendar days minus 2 rain days = **8 laytime days used** — rain days don't count against the charterer. So she is **2 days over: charterer owes the owner 2 × $18,500 = $37,000 demurrage**. (Had the rain not been excluded, the bill would have read 4 days = $74,000 — the weather clause is worth $37,000 here.) The deciding document is the **statement of facts**, the port's signed log of NOR, berthing, work and stoppages. Demurrage disputes are won and lost on it, line by line.
95
+
96
+ **A3.** Ranked by size: **(1) Cancellation into a rallied market** — if you cannot load by the cancelling date, the buyer can cancel and buy replacement. You are left owning ~40,000 t while your sale disappears; if your purchases were hedged with short futures, those shorts are 40¢ against you on the missing 20,000 t you now must buy at post-rally differentials — and a default/washout settlement would reference the market having moved ≈ 40¢ × 2.2 M bu ≈ **$880,000** on the full cargo if the whole contract fails. **(2) Demurrage** — the vessel has waited since the 18th; every day beyond laytime at $24,000 accrues to your account because the delay is cargo-side: a week is **$168,000**. **(3) Carrying and replacement costs** on the 40,000 t you do hold (~$17.4M financed ≈ $2,900/day). The lesson: the flat-price move you thought you had hedged away comes back through the execution failure — that is how a date becomes a liability.
97
+
98
+ **A4.** You are short September; rolling means **buying September back and selling December**. December is 15¢ *above* — you re-sell higher than you buy back: you **receive** (capture) the 15 cents. That is the mechanics behind ep 3's store-or-sell rule: in a carry market, a short hedge *earns the spread* every roll, which is precisely the market paying you for storing hedged inventory. (In an inverse the same roll bleeds — same plumbing, opposite sign.)
99
+
100
+ **A5.** (a) 100 lots × 5,000 bu × $0.2025 = **$101,250 of variation margin, wired same day**. (b) Because the physical gain is unrealized — it arrives when the grain is sold — while the margin call is cash *today*; funding cost is real, and volatile markets also bring initial-margin increases. (c) **A hedge converts price risk into liquidity risk** — the position is fine, the cash flow is not.
101
+
102
+ **A6.** Matif wheat is **50 t per lot**: 50 lots = **2,500 t**. Chicago is **5,000 bu per lot**: 50 lots = 250,000 bu ≈ **6,800 t** (÷36.7). The book is nowhere near flat: the Chicago leg is ~2.7× the Matif leg in tonnage. And even at equal tonnage it wouldn't be flat: different wheats (SRW vs EU milling), different currencies (¢/bu vs €/t), different delivery points — an inter-exchange *spread*, not a hedge. "Fifty lots" is not a size until you know the contract.
103
+
104
+ **A7.** Someone **bids $4.80/bu for 50 lots (250,000 bu) of December corn futures** and simultaneously **offers at $4.80½**. "The half's workable" means the offer at 4.80½ is negotiable — the seller would likely trade inside it (say 4.80¼) if firm interest shows. Nothing has traded yet: "done" is the word that seals it. The half-cent between them is 50 lots × 5,000 bu × $0.005 = **$1,250** — small words, real money.
105
+
106
+ ---
107
+
108
+ ## The episode, in writing
109
+
110
+ ### The tape: a friendly report, and a port gone dark
111
+
112
+ The August WASDE cut the US corn yield to **180.7 bu/acre** — below the average trade guess of 182.5 and near the bottom of the 180–185 range — and took new-crop ending stocks from 1.79 down to 1.653 billion bushels. December corn jumped 20¼¢ to $4.80¾, a two-week high.
113
+
114
+ Soybeans printed the opposite supply story — production 44 million bushels above July, 41 above the trade — and *still* closed up 14½¢ at $11.83¼: USDA raised crush by 30 million bushels, and corn dragged the floor higher. The market trades the whole balance sheet, not one row.
115
+
116
+ Wheat rose 22½¢ in Chicago — half report, half Black Sea. Tuesday's safe-passage rumour died overnight when drones idled Novorossiysk's two big grain terminals (15.5+ Mt/yr combined capacity). Tuesday priced trapped grain getting out; Wednesday priced Russian loading capacity going dark. Same mechanism, reverse gear. Meanwhile a queue of vessels sits at anchor off the port — each one on a clock denominated in dollars per day. That clock is this episode.
117
+
118
+ ### Three letters that allocate a trade
119
+
120
+ Strip a physical trade to its skeleton and three questions remain: who arranges the ship, who insures the cargo, and at what exact moment it stops being the seller's problem. **Incoterms** — the standard vocabulary kept by the International Chamber of Commerce — answer all three in three letters.
121
+
122
+ | Term | Freight | Insurance | Risk passes |
123
+ |---|---|---|---|
124
+ | **FOB** — free on board | Buyer | Buyer | At loading |
125
+ | **CFR** — cost and freight | **Seller** | Buyer | At loading |
126
+ | **CIF** — cost, insurance, freight | **Seller** | **Seller** (for buyer's benefit) | At loading |
127
+
128
+ Read the last column. It is the same in all three rows, and it is the detail that catches every newcomer: on a CIF cargo the seller pays the freight to Qingdao and buys the insurance — yet the voyage runs at the **buyer's risk**. If the ship founders mid-ocean, the loss is the buyer's, and the buyer claims on the very policy the seller bought. **Cost and risk travel separately.** The letters tell you who pays; they also tell you, quietly, who is exposed.
129
+
130
+ Why would anyone buy FOB rather than CIF? Control, and freight. An importer with its own chartering desk buys FOB and keeps the freight economics; a buyer without one pays up for CIF and outsources the problem. Freight is a market of its own, and whoever fixes the ship carries that market's risk — episode 10's subject.
131
+
132
+ ### The clock: one cargo, from fixture to demurrage
133
+
134
+ Take the episode-2 cargo one step further down the pipe: **60,000 t of soybeans, FOB Santos**, sold at a differential against November. At Wednesday's board (~$434/t), that is a **$26 million object**.
135
+
136
+ The execution chain is a sequence of dated, contractual events:
137
+
138
+ | Step | What happens | The clock |
139
+ |---|---|---|
140
+ | Laycan | Loading window agreed: 15–25 Nov | Vessel must present inside it |
141
+ | Nomination | Buyer names the performing vessel | — |
142
+ | Arrival | Vessel arrives the 18th, master tenders **NOR** | Clock armed |
143
+ | Laytime | 10,000 t per weather working day → 6 days allowed | Clock running |
144
+ | The queue | Santos line-up: loading takes 9 days | 3 days over |
145
+ | Demurrage | Charter party rate $24,000/day | **$72,000** |
146
+
147
+ Rain matters: laytime is counted in **weather working days**, so a rain-stopped day does not tick. And the clock runs both ways — beat laytime and the owner pays **despatch**, customarily half the demurrage rate.
148
+
149
+ Now set the delay against the economics of the trade:
150
+
151
+ ```chart
152
+ {"type":"waterfall","unit":"$ thousand","title":"Three days on the clock, one Panamax of beans",
153
+ "caption":"The board didn't move and the differential didn't move - the margin still lost 13%.",
154
+ "source":"Worked example, episodes 2 and 4 (beans at Wed close ~$434/t)",
155
+ "steps":[{"label":"Trading margin (30c/bu)","value":660,"kind":"base"},
156
+ {"label":"Demurrage 3 x $24k","value":-72},
157
+ {"label":"Interest, 3 days","value":-13},
158
+ {"label":"What's left","kind":"total"}]}
159
+ ```
160
+
161
+ Financing alone — $26M at 6% — runs over **$4,000 a day** whether anything goes wrong or not. The meter never stops.
162
+
163
+ On the desk, those three days sound like this:
164
+
165
+ > **OPS:** She tendered NOR at 06:00. Terminal gives us a berth Saturday.
166
+ > **TRADER:** When does the clock start?
167
+ > **OPS:** It's already running. Turn time expired at noon.
168
+ > **TRADER:** What did we fix her at?
169
+ > **OPS:** Twenty-four a day. Line-up says three over if the queue doesn't move.
170
+ > **TRADER:** That's seventy-two. Send me the line-up and get on to the terminal.
171
+
172
+ Nobody mentioned the cargo or the price. "Twenty-four a day" is $24,000 of demurrage; "three over" is three days beyond laytime. The conversation is entirely about time, because time is the only thing still moving.
173
+
174
+ ### Who pays, and how a date becomes a liability
175
+
176
+ Under FOB the buyer holds the charter, so the shipowner invoices the buyer — but if the delay was cargo-side, the claim gets passed up the sales contract. Demurrage claims cascade through whole strings of contracts and are fought line by line, months later, on the **statement of facts**, the port's log of everything that happened and when.
177
+
178
+ The sharper edge is the **laycan** itself. Miss it as the buyer — vessel presents on the 26th — and the seller can cancel and resell. Miss it as the seller — cargo not ready when the ship is — and you pay the ship to wait, or face a cancelled contract in a market that has moved against you. If the board rallied 40¢ while you fumbled, a one-day slip puts the whole flat-price move on your book, unhedged. Execution failures become contractual liabilities not through drama, but through a date.
179
+
180
+ ### The cargo becomes paper
181
+
182
+ At loading, three documents replace the physical:
183
+
184
+ - **Draft survey** — the cargo is weighed by reading the ship's displacement before and after loading; the difference is the cargo.
185
+ - **Quality certificate** — issued at the load port, and in most grain contracts **final**: if discharge finds something different, the load-port certificate still governs.
186
+ - **Bill of lading** — receipt, contract of carriage and **document of title** in one. Whoever holds it owns the cargo: $26 million moving at the speed of a courier envelope.
187
+
188
+ The date on a bill of lading proves shipment inside the contract window. Backdating one by a single day is not sloppiness — it is fraud, and it has sunk trading houses.
189
+
190
+ That, finally, is why execution desks are a profit centre and not admin: a good operator wins the demurrage claim, earns the despatch, and saves the day the trader gave away in the negotiation. None of it shows on a screen — which is exactly why the margin lives there.
191
+
192
+ **Tomorrow:** wheat — the map and the screens. Why two wheats at the same flat price are not the same wheat, and why it takes three exchanges to price one grain.
package/ep04.mp3 ADDED
Binary file
@@ -0,0 +1,69 @@
1
+ Here is a number the screen never shows you. Twenty-four thousand dollars. ||| 0.4
2
+ That is what one idle ship can cost. Per day. While everyone stands around waiting. ||| 0.6
3
+ This is Soft Commodity Trading, episode four. The physical chain, end to end. Who owns the cargo, who owns the risk, and how three quiet days at a port become a six-figure invoice. ||| 0.8
4
+ First, the tape. Yesterday had two stories, and they landed on the same wheat contract. ||| 0.5
5
+ The August W A S D E was friendly. The U S D A cut its corn yield to one hundred eighty point seven bushels an acre. The trade had guessed one eighty-two and a half. The standing number was one eighty-three. ||| 0.4
6
+ Ending stocks fell from one point seven nine billion bushels to one point six five. December corn jumped twenty and a quarter cents, to four eighty and three quarters. A two-week high. ||| 0.5
7
+ Soybeans were the strange one. The report made the crop bigger. Production came in forty-four million bushels above July, forty-one million above the trade guess. ||| 0.35
8
+ And November beans still closed up fourteen and a half, at eleven eighty-three and a quarter. The U S D A raised crush by thirty million bushels, and corn pulled the whole floor higher. ||| 0.4
9
+ A bearish supply line and a friendly close. You trade the whole balance sheet, not one row of it. ||| 0.6
10
+ Then wheat. Up twenty-two and a half cents in Chicago, to six fifty-two and three quarters. Kansas City up twenty-one and a half. Half of that is the report. The other half is the Black Sea. ||| 0.5
11
+ Tuesday's rumour of safe-passage talks died overnight. Ukrainian drones hit Novorossiysk and idled its two big grain terminals. Combined capacity, more than fifteen million tonnes a year. ||| 0.4
12
+ On Tuesday the market priced trapped grain getting out. On Wednesday it priced Russian loading capacity going dark. Same mechanism we walked through yesterday. Running in reverse. ||| 0.5
13
+ Turkey is floating a plan for two protected corridors, one for each coast. Nobody has signed anything. ||| 0.5
14
+ And think about the ships. Every vessel anchored off Novorossiysk this morning is on a clock. The clock is contractual, and it is denominated in dollars per day. ||| 0.4
15
+ That clock is today's subject. ||| 0.8
16
+ Strip a physical trade down to its skeleton and three questions are left. Who arranges the ship. Who insures the cargo. And at what exact moment it stops being the seller's problem. ||| 0.5
17
+ The trade answers all three with three letters. Incoterms. A standard vocabulary of risk allocation, kept by the International Chamber of Commerce. ||| 0.5
18
+ F O B, free on board, you know from episode two. The seller delivers the cargo over the ship's rail at the load port. From that moment it travels at the buyer's risk. The buyer charters the vessel. The buyer insures. The buyer owns the voyage. ||| 0.5
19
+ C F R is cost and freight. Now the seller pays for the voyage. They charter the ship and deliver it to a named destination port. ||| 0.35
20
+ And C I F adds one word. Cost, insurance, freight. The seller also buys the marine insurance. ||| 0.6
21
+ Now the detail that catches every newcomer. In all three terms, risk passes at the load port. ||| 0.4
22
+ Sit with that. On a C I F cargo, the seller pays the freight all the way to Qingdao. The seller buys the insurance. And the cargo still travels at the buyer's risk. ||| 0.4
23
+ If the ship founders mid-ocean, that is the buyer's loss. The buyer claims on the very policy the seller bought. ||| 0.4
24
+ Cost and risk travel separately. The letters tell you who pays. They also tell you, quietly, who is exposed. ||| 0.7
25
+ So why would anyone buy F O B instead of C I F? Control, and freight. ||| 0.35
26
+ An importer with its own chartering desk buys F O B and keeps the freight economics for itself. A buyer with no shipping desk pays up for C I F and outsources the problem. Freight is a market of its own, and whoever fixes the ship carries that market's risk. More on that in episode ten. ||| 0.7
27
+ Now put a real cargo through the chain. Sixty thousand tonnes of soybeans, F O B Santos, sold at a differential against November. The episode two cargo, one step further down the pipe. ||| 0.5
28
+ At yesterday's board, beans are worth about four hundred thirty-four dollars a tonne. So this is a twenty-six million dollar object. Hold that number. ||| 0.6
29
+ The contract gives a laycan, the loading window. Fifteenth to the twenty-fifth of November. ||| 0.35
30
+ The buyer nominates a vessel. Names her to the seller, and she must present inside that window. ||| 0.4
31
+ She arrives on the eighteenth. The master tenders notice of readiness. N O R. The formal declaration, I have arrived, and I am ready to load. ||| 0.4
32
+ That piece of paper starts the clock. ||| 0.6
33
+ The clock is called laytime, the time the contract allows for loading. Say ten thousand tonnes per weather working day. A day that counts only if the weather lets you work. Rain stops the work, and rain stops the clock. ||| 0.4
34
+ Sixty thousand tonnes at ten thousand a day. Six days of allowed laytime. ||| 0.5
35
+ But Santos has a queue. Remember the line-up from episode two, the list of vessels waiting for a berth. Loading takes nine days instead of six. ||| 0.5
36
+ Three days over. The charter party, the contract hiring the ship itself, fixes demurrage at twenty-four thousand dollars a day. Demurrage, the penalty for holding a vessel beyond her laytime. ||| 0.4
37
+ Three days. Seventy-two thousand dollars. ||| 0.7
38
+ Here is how those three days sound on the desk. ||| 0.5
39
+ OPS: She tendered N O R at oh six hundred. Terminal gives us a berth Saturday. ||| 0.25
40
+ TRADER: When does the clock start? ||| 0.25
41
+ OPS: It's already running. Turn time expired at noon. ||| 0.25
42
+ TRADER: What did we fix her at? ||| 0.25
43
+ OPS: Twenty-four a day. Line-up says three over if the queue doesn't move. ||| 0.25
44
+ TRADER: That's seventy-two. Send me the line-up and get on to the terminal. ||| 0.6
45
+ Notice what was not said. Nobody mentioned the cargo, or the price. Twenty-four a day is twenty-four thousand dollars of demurrage. Three over is three days beyond laytime. The whole conversation is about time, because time is the only thing still moving. ||| 0.7
46
+ Now set seventy-two thousand against the trade. ||| 0.4
47
+ Episode two's margin on this cargo was thirty cents a bushel. Six hundred sixty thousand dollars. The market has not moved. The differential has not moved. And eleven percent of the margin is gone. ||| 0.5
48
+ And underneath, the quiet cost. Twenty-six million dollars of beans, financed at six percent, is over four thousand dollars a day of interest. That meter never stops at all. ||| 0.6
49
+ It cuts the other way too. Load faster than laytime and the shipowner pays you despatch. Customarily half the demurrage rate. ||| 0.4
50
+ A terminal that turns the ship around in four days earns real money for the charterer. Which is why an execution desk is a profit centre, not admin. A good operator wins the demurrage claim, earns the despatch, and saves the day the trader gave away in the negotiation. ||| 0.7
51
+ So who actually pays the seventy-two thousand? Under F O B, the buyer holds the charter, so the shipowner invoices the buyer. ||| 0.4
52
+ But if the delay was the seller's fault, cargo not ready, documents late, the buyer passes the claim up the sales contract. Demurrage claims cascade through a whole string of contracts. They are fought line by line, months later, and the battlefield is a document called the statement of facts, the port's log of everything that happened and when. ||| 0.7
53
+ The sharper edge is the laycan itself. ||| 0.4
54
+ Miss it as the buyer, your nominated vessel shows up on the twenty-sixth, and the seller can walk. Cancel, and resell the cargo. ||| 0.4
55
+ Miss it as the seller, cargo not ready when the ship is, and you are paying that ship to sit, or facing a cancelled contract in a market that moved against you. ||| 0.4
56
+ If the board rallied forty cents while you fumbled, a one-day slip just put the whole flat-price move on your book, unhedged. That is how a small execution failure becomes a contractual liability. Not through drama. Through a date. ||| 0.8
57
+ One more layer. The paper. ||| 0.5
58
+ At loading, the cargo becomes documents. A draft survey fixes the weight, read the ship's displacement before loading and after, the difference is the cargo. ||| 0.4
59
+ A quality certificate is issued at the load port, and in most grain contracts that certificate is final. If discharge finds something different, the load-port certificate still governs. ||| 0.5
60
+ And the bill of lading. Receipt for the goods, contract of carriage, and document of title, all in one piece of paper. Whoever holds the bill owns the cargo. ||| 0.4
61
+ Twenty-six million dollars, moving at the speed of a courier envelope. ||| 0.6
62
+ The date on that bill is sacred. It proves the cargo shipped inside the contract window. Backdating a bill of lading by one day is not sloppiness. It is fraud, and it has sunk trading houses. ||| 0.8
63
+ So what to keep from today. ||| 0.5
64
+ Three letters allocate the trade. F O B, C F R, C I F. And they allocate cost and risk separately. Risk passes at the ship's rail even when the seller is paying the freight. ||| 0.5
65
+ Execution is a clock. Laycan, nomination, notice of readiness, laytime, then demurrage or despatch. Every tick of it is contractual money. ||| 0.5
66
+ And the cargo is its paper. Draft survey for weight. Certificate final at load for quality. The bill of lading for title. ||| 0.5
67
+ None of this shows on a screen. Which is exactly why the margin lives here. ||| 0.7
68
+ Tomorrow, wheat. The map and the screens. Why two wheats at the same flat price are not the same wheat, and why it takes three exchanges to price one grain. ||| 0.5
69
+ The quiz is in the notes and the email. Three days on the clock, and a laycan question with teeth. This was Soft Commodity Trading. See you tomorrow. ||| 0.5
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Soft Commodity Trading - continuity fetcher.
4
+
5
+ Pulls what the show has already aired into the working directory, so today's
6
+ episode builds on real history instead of guessing:
7
+
8
+ covered.md running log of every episode aired and what it covered
9
+ prev_epNN.md notes for the J-1 and J-3 episodes, for the quiz
10
+
11
+ Notes for older episodes live in the npm versions that published them, so this
12
+ walks back through published versions until it finds each one.
13
+
14
+ Usage:
15
+ python3 fetch_context.py --episode 12 --outdir .
16
+
17
+ Exit code is always 0: missing history is normal early on and must never block
18
+ an episode. Prints FETCHED= / MISSING= lines so the caller knows what it got.
19
+ """
20
+ import argparse
21
+ import json
22
+ import os
23
+ import sys
24
+ import tarfile
25
+ import tempfile
26
+ import urllib.request
27
+
28
+ PKG = "@sdelsad/commodity-desk-daily"
29
+ REGISTRY = "https://registry.npmjs.org"
30
+
31
+
32
+ def registry_meta():
33
+ try:
34
+ with urllib.request.urlopen(f"{REGISTRY}/{PKG.replace('/', '%2f')}", timeout=30) as r:
35
+ return json.load(r)
36
+ except Exception as exc:
37
+ print(f" ! registry unreachable ({exc})")
38
+ return None
39
+
40
+
41
+ def sort_versions(versions):
42
+ def key(v):
43
+ try:
44
+ return tuple(int(x) for x in v.split("."))
45
+ except ValueError:
46
+ return (0, 0, 0)
47
+ return sorted(versions, key=key, reverse=True)
48
+
49
+
50
+ def extract_from(version, wanted, outdir, workdir):
51
+ """Extract any of `wanted` filenames present in this version's tarball."""
52
+ name = PKG.split("/")[-1]
53
+ url = f"{REGISTRY}/{PKG}/-/{name}-{version}.tgz"
54
+ tgz = os.path.join(workdir, f"{version}.tgz")
55
+ found = {}
56
+ try:
57
+ urllib.request.urlretrieve(url, tgz)
58
+ with tarfile.open(tgz) as tar:
59
+ for member in tar.getmembers():
60
+ base = os.path.basename(member.name)
61
+ if base in wanted and base not in found:
62
+ fh = tar.extractfile(member)
63
+ if fh:
64
+ found[base] = fh.read()
65
+ except Exception:
66
+ pass
67
+ finally:
68
+ if os.path.exists(tgz):
69
+ os.unlink(tgz)
70
+ return found
71
+
72
+
73
+ def main():
74
+ ap = argparse.ArgumentParser()
75
+ ap.add_argument("--episode", type=int, required=True)
76
+ ap.add_argument("--outdir", default=".")
77
+ ap.add_argument("--max-versions", type=int, default=25,
78
+ help="how far back to walk through published versions")
79
+ args = ap.parse_args()
80
+
81
+ os.makedirs(args.outdir, exist_ok=True)
82
+ meta = registry_meta()
83
+ if not meta:
84
+ print("MISSING=covered.md")
85
+ return 0
86
+
87
+ versions = sort_versions(list(meta.get("versions", {}).keys()))[: args.max_versions]
88
+
89
+ # which episode notes we still want
90
+ targets = {}
91
+ for offset in (1, 3):
92
+ n = args.episode - offset
93
+ if n >= 1:
94
+ targets[f"ep{n:02d}.md"] = f"prev_ep{n:02d}.md"
95
+
96
+ want = set(targets) | {"covered.md", "glossary.md"}
97
+ got = {}
98
+
99
+ workdir = tempfile.mkdtemp(prefix="cdd-ctx-")
100
+ try:
101
+ for version in versions:
102
+ if not want - set(got):
103
+ break
104
+ for base, blob in extract_from(version, want - set(got), args.outdir, workdir).items():
105
+ got[base] = blob
106
+ finally:
107
+ import shutil
108
+ shutil.rmtree(workdir, ignore_errors=True)
109
+
110
+ for base, blob in got.items():
111
+ outname = targets.get(base, base)
112
+ path = os.path.join(args.outdir, outname)
113
+ with open(path, "wb") as fh:
114
+ fh.write(blob)
115
+ print(f"FETCHED={outname} ({len(blob):,} bytes)")
116
+
117
+ for base in want - set(got):
118
+ print(f"MISSING={targets.get(base, base)}")
119
+ return 0
120
+
121
+
122
+ if __name__ == "__main__":
123
+ sys.exit(main())
@@ -0,0 +1,120 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Soft Commodity Trading - audio generator.
4
+
5
+ Turns a segmented spoken script into a podcast-ready mp3, with calibrated
6
+ pauses between segments so the narration breathes like a human reading.
7
+
8
+ Script format: one segment per line,
9
+ TEXT ||| PAUSE_SECONDS
10
+ Blank lines and lines starting with '#' are ignored. A line with no '|||'
11
+ gets DEFAULT_PAUSE.
12
+
13
+ Usage:
14
+ python3 generate_audio.py script.txt ep02.mp3 [--voice am_michael] [--speed 0.97]
15
+
16
+ Exit codes: 0 ok, 1 usage/IO error, 2 synthesis error.
17
+ """
18
+ import argparse
19
+ import os
20
+ import subprocess
21
+ import sys
22
+ import tempfile
23
+
24
+ DEFAULT_PAUSE = 0.4
25
+ MAX_PAUSE = 3.0
26
+
27
+
28
+ def parse_script(path):
29
+ """Return [(text, pause_seconds), ...] from a segmented script file."""
30
+ segments = []
31
+ with open(path, encoding="utf-8") as fh:
32
+ for lineno, raw in enumerate(fh, 1):
33
+ line = raw.strip()
34
+ if not line or line.startswith("#"):
35
+ continue
36
+ if "|||" in line:
37
+ text, _, pause_str = line.rpartition("|||")
38
+ text = text.strip()
39
+ try:
40
+ pause = float(pause_str.strip())
41
+ except ValueError:
42
+ print(f" ! line {lineno}: bad pause {pause_str!r}, using default")
43
+ pause = DEFAULT_PAUSE
44
+ else:
45
+ text, pause = line, DEFAULT_PAUSE
46
+ if not text:
47
+ continue
48
+ segments.append((text, max(0.0, min(pause, MAX_PAUSE))))
49
+ return segments
50
+
51
+
52
+ def main():
53
+ ap = argparse.ArgumentParser()
54
+ ap.add_argument("script")
55
+ ap.add_argument("output", help="output .mp3 path")
56
+ ap.add_argument("--voice", default="am_michael")
57
+ ap.add_argument("--speed", type=float, default=0.97)
58
+ ap.add_argument("--bitrate", default="96k")
59
+ ap.add_argument("--kokoro-dir", default=os.path.expanduser("~/kokoro"))
60
+ args = ap.parse_args()
61
+
62
+ try:
63
+ import numpy as np
64
+ import soundfile as sf
65
+ from kokoro_onnx import Kokoro
66
+ except ImportError as exc:
67
+ print(f"Missing dependency ({exc}). Run setup.sh first.", file=sys.stderr)
68
+ return 1
69
+
70
+ segments = parse_script(args.script)
71
+ if not segments:
72
+ print(f"No usable segments in {args.script}", file=sys.stderr)
73
+ return 1
74
+ words = sum(len(t.split()) for t, _ in segments)
75
+ print(f"[audio] {len(segments)} segments, {words} words, voice={args.voice}")
76
+
77
+ model = os.path.join(args.kokoro_dir, "kokoro-v1.0.onnx")
78
+ voices = os.path.join(args.kokoro_dir, "voices-v1.0.bin")
79
+ for path in (model, voices):
80
+ if not os.path.exists(path):
81
+ print(f"Missing model file {path}. Run setup.sh first.", file=sys.stderr)
82
+ return 1
83
+
84
+ kokoro = Kokoro(model, voices)
85
+ chunks, rate, failures = [], 24000, 0
86
+ for i, (text, pause) in enumerate(segments, 1):
87
+ try:
88
+ samples, rate = kokoro.create(text, voice=args.voice, speed=args.speed)
89
+ chunks.append(samples)
90
+ except Exception as exc: # one bad segment must not kill the episode
91
+ failures += 1
92
+ print(f" ! segment {i} failed ({exc}); skipping")
93
+ chunks.append(np.zeros(int(rate * pause), dtype="float32"))
94
+ if i % 10 == 0 or i == len(segments):
95
+ print(f" .. {i}/{len(segments)}")
96
+
97
+ if failures == len(segments):
98
+ print("All segments failed.", file=sys.stderr)
99
+ return 2
100
+
101
+ audio = np.concatenate(chunks)
102
+ minutes = len(audio) / rate / 60
103
+ with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tmp:
104
+ wav = tmp.name
105
+ sf.write(wav, audio, rate)
106
+ subprocess.run(
107
+ ["ffmpeg", "-y", "-loglevel", "error", "-i", wav, "-b:a", args.bitrate, args.output],
108
+ check=True,
109
+ )
110
+ os.unlink(wav)
111
+
112
+ size = os.path.getsize(args.output)
113
+ print(f"[audio] {args.output} | {minutes:.1f} min | {size:,} bytes | {failures} failed segment(s)")
114
+ print(f"DURATION_SECONDS={int(len(audio) / rate)}")
115
+ print(f"SIZE_BYTES={size}")
116
+ return 0
117
+
118
+
119
+ if __name__ == "__main__":
120
+ sys.exit(main())
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
- {
2
- "name": "@sdelsad/commodity-desk-daily",
3
- "version": "1.0.17",
4
- "description": "Soft Commodity Trading - Ep 5: Wheat: The Map and the Screens",
5
- "license": "CC-BY-4.0",
6
- "keywords": [
7
- "podcast",
8
- "commodities",
9
- "trading",
10
- "education"
11
- ]
12
- }
1
+ {
2
+ "name": "@sdelsad/commodity-desk-daily",
3
+ "version": "1.13.2",
4
+ "description": "Soft Commodity Trading - Ep 5: Wheat: The Map and the Screens",
5
+ "license": "CC-BY-4.0",
6
+ "keywords": [
7
+ "podcast",
8
+ "commodities",
9
+ "trading",
10
+ "education"
11
+ ]
12
+ }