Skip to content

Commit

Permalink
Add volume to cash index
Browse files Browse the repository at this point in the history
  • Loading branch information
kirkedev committed Mar 16, 2024
1 parent 9309b0b commit 2410c2a
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 26 deletions.
4 changes: 2 additions & 2 deletions lib/cutout/CutoutIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { round } from "..";
import Series, { type Observation } from "../time/Series";
import { sumBy } from "../itertools/accumulate";
import map from "../itertools/map";
import rolling from "../itertools/rolling";
import window from "../itertools/window";
import type Cutout from ".";

interface CutoutIndex extends Observation {
Expand All @@ -21,7 +21,7 @@ const avgPrice = (records: Cutout[]): number =>
sumBy(records, totalValue) / sumBy(records, totalLoads);

const cutoutIndex = (cutout: Iterable<Cutout>): Iterable<CutoutIndex> =>
map(rolling(Series.sort(cutout), 5), function(records) {
map(window(Series.sort(cutout), 5), function(records) {
const record = records[records.length - 1];

return {
Expand Down
4 changes: 2 additions & 2 deletions lib/itertools/rolling.ts → lib/itertools/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class WindowedIterable<T> implements Iterable<T[]> {
iterateWindows(iterate(this.iterable), this.size);
}

const rolling = <T>(iterable: Iterable<T>, size: number): Iterable<T[]> =>
const window = <T>(iterable: Iterable<T>, size: number): Iterable<T[]> =>
new WindowedIterable(iterable, size);

export default rolling;
export default window;
1 change: 1 addition & 0 deletions lib/mpr/PurchaseType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@ enum Basis {
}

export { Seller, Arrangement, Basis };

export type PurchaseType = [Seller, Arrangement, Basis];
43 changes: 28 additions & 15 deletions lib/slaughter/CashIndex.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import { isSameDay } from "date-fns";
import { round } from "..";
import Series, { type Observation } from "../time/Series";
import { Arrangement } from "../mpr/PurchaseType";
import { sumBy } from "../itertools/accumulate";
import filter from "../itertools/filter";
import groupBy from "../itertools/groupBy";
import map from "../itertools/map";
import rolling from "../itertools/rolling";
import window from "../itertools/window";
import { Arrangement } from "../mpr/PurchaseType";
import Series, { type Observation } from "../time/Series";
import type Slaughter from ".";

interface CashIndex extends Observation {
dailyPrice: number;
indexPrice: number;
volume: number;
}

interface Values extends Observation {
Expand All @@ -21,8 +22,6 @@ interface Values extends Observation {
netPrice: number;
}

const arrangements = [Arrangement.Negotiated, Arrangement.MarketFormula, Arrangement.NegotiatedFormula];

const weight = (slaughter: Values): number =>
slaughter.headCount * slaughter.carcassWeight;

Expand All @@ -32,26 +31,35 @@ const value = (slaughter: Values): number =>
const avgPrice = (value: number, weight: number): number =>
round(value / weight);

const filterSlaughter = (slaughter: Iterable<Slaughter>): Iterable<Values> =>
filter(slaughter, ({ netPrice, carcassWeight, arrangement }) =>
carcassWeight != null && netPrice != null && arrangements.includes(arrangement)) as Iterable<Values>;
const volume = (slaughter: Slaughter): number =>
slaughter.headCount;

function cashIndex(records: Iterable<Slaughter>): Iterable<CashIndex> {
const slaughter = Series.sort(filterSlaughter(records));
const dates = groupBy(slaughter, (last, current) => isSameDay(current.date, last.date));
function cashIndex(slaughter: Iterable<Slaughter>): Iterable<CashIndex> {
const dates = groupBy(Series.sort(slaughter), (last, current) => isSameDay(current.date, last.date));

const totals = map(dates, slaughter => {
const totals = map(dates, function(slaughter) {
const { date } = slaughter[0];

const values = filter(slaughter, slaughter =>
slaughter.arrangement === Arrangement.Negotiated ||
slaughter.arrangement === Arrangement.MarketFormula ||
slaughter.arrangement === Arrangement.NegotiatedFormula) as Iterable<Values>;

const summaries = filter(slaughter, slaughter =>
slaughter.arrangement === Arrangement.All ||
slaughter.arrangement === Arrangement.PackerOwned);

return {
date,
weight: sumBy(slaughter, weight),
value: sumBy(slaughter, value)
volume: sumBy(summaries, volume),
weight: sumBy(values, weight),
value: sumBy(values, value)
};
});

return map(rolling(totals, 2), ([last, { date, weight, value }]) => ({
return map(window(totals, 2), ([last, { date, weight, value, volume }]) => ({
date,
volume,
dailyPrice: avgPrice(value, weight),
indexPrice: avgPrice(last.value + value, last.weight + weight)
}));
Expand All @@ -69,6 +77,11 @@ namespace CashIndex {
Array.from(map(cash, ({ date, dailyPrice: value }) => ({
date, value
})));

export const volume = (cash: Iterable<CashIndex>): Series =>
Array.from(map(cash, ({ date, volume: value }) => ({
date, value
})));
}

export default CashIndex;
15 changes: 10 additions & 5 deletions test/api/slaughter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,32 @@ describe("slaughter api", () => {
// https://www.cmegroup.com/ftp/cash_settled_commodity_index_prices/daily_data/lean_hogs/2021/LH210809.txt
date: new Date(2021, 7, 9),
dailyPrice: 110.59,
indexPrice: 110.77
indexPrice: 110.77,
volume: 417545
}, {
// https://www.cmegroup.com/ftp/cash_settled_commodity_index_prices/daily_data/lean_hogs/2021/LH210810.txt
date: new Date(2021, 7, 10),
dailyPrice: 110.32,
indexPrice: 110.45
indexPrice: 110.45,
volume: 434993
}, {
// https://www.cmegroup.com/ftp/cash_settled_commodity_index_prices/daily_data/lean_hogs/2021/LH210811.txt
date: new Date(2021, 7, 11),
dailyPrice: 110.06,
indexPrice: 110.19
indexPrice: 110.19,
volume: 436525
}, {
// https://www.cmegroup.com/ftp/cash_settled_commodity_index_prices/daily_data/lean_hogs/2021/LH210812.txt
date: new Date(2021, 7, 12),
dailyPrice: 109.75,
indexPrice: 109.90
indexPrice: 109.90,
volume: 430424
}, {
// https://www.cmegroup.com/ftp/cash_settled_commodity_index_prices/daily_data/lean_hogs/2021/LH210813.txt
date: new Date(2021, 7, 13),
dailyPrice: 109.59,
indexPrice: 109.67
indexPrice: 109.67,
volume: 497898
}]);
});
});
4 changes: 2 additions & 2 deletions test/lib/itertools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import filter, { find } from "lib/itertools/filter";
import { all, count, countIf, each, max, maxBy, min, minBy, none, one, some, sumBy } from "lib/itertools/accumulate";
import zip from "lib/itertools/zip";
import groupBy from "lib/itertools/groupBy";
import rolling from "lib/itertools/rolling";
import window from "lib/itertools/window";

test("chunk a date range by size", () => {
const start = new Date(2019, 5, 1);
Expand Down Expand Up @@ -260,7 +260,7 @@ test("run a callback against each item in an iterable", () => {

test("create a moving window of an iterable", () => {
const range = ["a", "b", "c", "d", "e", "f", "g", "h"];
const windows = Array.from(rolling(range, 3));
const windows = Array.from(window(range, 3));

expect(windows).toEqual([
["a", "b", "c"],
Expand Down
14 changes: 14 additions & 0 deletions test/lib/slaughter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 1));
expect(record.dailyPrice).toBeCloseTo(57.45);
expect(record.indexPrice).toBeCloseTo(57.41);
expect(record.volume).toBe(743_615);
});

test("Lean Hog Index for 2/4/2019", () => {
Expand All @@ -166,6 +167,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 4));
expect(record.dailyPrice).toBeCloseTo(57.18);
expect(record.indexPrice).toBeCloseTo(57.36);
expect(record.volume).toBe(435_442);
});

test("Lean Hog Index for 2/5/2019", () => {
Expand All @@ -174,6 +176,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 5));
expect(record.dailyPrice).toBeCloseTo(57.13);
expect(record.indexPrice).toBeCloseTo(57.16);
expect(record.volume).toBe(442_685);
});

test("Lean Hog Index for 2/6/2019", () => {
Expand All @@ -182,6 +185,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 6));
expect(record.dailyPrice).toBeCloseTo(56.65);
expect(record.indexPrice).toBeCloseTo(56.89);
expect(record.volume).toBe(435_426);
});

test("Lean Hog Index for 2/7/2019", () => {
Expand All @@ -190,6 +194,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 7));
expect(record.dailyPrice).toBeCloseTo(56.40);
expect(record.indexPrice).toBeCloseTo(56.53);
expect(record.volume).toBe(406_406);
});

test("Lean Hog Index for 2/8/2019", () => {
Expand All @@ -198,6 +203,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 8));
expect(record.dailyPrice).toBeCloseTo(55.98);
expect(record.indexPrice).toBeCloseTo(56.14);
expect(record.volume).toBe(605_024);
});

test("Lean Hog Index for 2/11/2019", () => {
Expand All @@ -206,6 +212,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 11));
expect(record.dailyPrice).toBeCloseTo(55.78);
expect(record.indexPrice).toBeCloseTo(55.91);
expect(record.volume).toBe(405_743);
});

test("Lean Hog Index for 2/12/2019", () => {
Expand All @@ -214,6 +221,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 12));
expect(record.dailyPrice).toBeCloseTo(55.32);
expect(record.indexPrice).toBeCloseTo(55.55);
expect(record.volume).toBe(418_607);
});

test("Lean Hog Index for 2/13/2019", () => {
Expand All @@ -222,6 +230,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 13));
expect(record.dailyPrice).toBeCloseTo(55.16);
expect(record.indexPrice).toBeCloseTo(55.24);
expect(record.volume).toBe(423_983);
});

test("Lean Hog Index for 2/14/2019", () => {
Expand All @@ -230,6 +239,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 14));
expect(record.dailyPrice).toBeCloseTo(54.89);
expect(record.indexPrice).toBeCloseTo(55.02);
expect(record.volume).toBe(433_976);
});

test("Lean Hog Index for 2/15/2019", () => {
Expand All @@ -238,6 +248,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 15));
expect(record.dailyPrice).toBeCloseTo(54.64);
expect(record.indexPrice).toBeCloseTo(54.74);
expect(record.volume).toBe(642_740);
});

test("Lean Hog Index for 2/18/2019", () => {
Expand All @@ -246,6 +257,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 18));
expect(record.dailyPrice).toBeCloseTo(54.08);
expect(record.indexPrice).toBeCloseTo(54.43);
expect(record.volume).toBe(420_891);
});

test("Lean Hog Index for 2/19/2019", () => {
Expand All @@ -254,6 +266,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 19));
expect(record.dailyPrice).toBeCloseTo(54.19);
expect(record.indexPrice).toBeCloseTo(54.13);
expect(record.volume).toBe(448_829);
});

test("Lean Hog Index for 2/20/2019", () => {
Expand All @@ -262,6 +275,7 @@ describe("Calculate the CME Lean Hog Index", () => {
expect(record.date).toEqual(new Date(2019, 1, 20));
expect(record.dailyPrice).toBeCloseTo(53.93);
expect(record.indexPrice).toBeCloseTo(54.06);
expect(record.volume).toBe(429_358);
});
});

Expand Down

0 comments on commit 2410c2a

Please sign in to comment.