diff --git a/docs/api-reference.md b/docs/api-reference.md index 1425503..c6f7a16 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -11,7 +11,7 @@ const client = new NodeTSNClient({ endpoint: "https://staging.tsn.truflation.com", signerInfo: { address: wallet.address, - signer: wallet, // Must implement signMessage (e.g. ethers Wallet) + signer: wallet, // Must implement signMessage (e.g. ethers Wallet) }, chainId: "tsn-1", }); @@ -32,21 +32,29 @@ const type = await stream.getType(); // Returns StreamType const records = await stream.getRecord({ dateFrom: "2024-01-01", dateTo: "2024-01-31", - frozenAt: 12345 // Optional block height + frozenAt: 12345, // Optional block height }); // Get stream index const index = await stream.getIndex({ - dateFrom: "2024-01-01", + dateFrom: "2024-01-01", dateTo: "2024-01-31", - baseDate: "2023-12-31" + baseDate: "2023-12-31", }); // Get first record const firstRecord = await stream.getFirstRecord({ afterDate: "2024-01-01", - frozenAt: 12345 + frozenAt: 12345, }); // Returns StreamRecord | null + +// Calculate year-over-year changes +const changes = await stream.getIndexChange({ + dateFrom: "2024-01-01", + dateTo: "2024-12-31", + daysInterval: 365, + baseDate: "2024-01-01", +}); // Returns StreamRecord[] ``` ### Primitive Stream Operations @@ -56,7 +64,7 @@ const primitiveStream = client.loadPrimitiveStream(streamLocator); // Insert data await primitiveStream.insertRecords([ - { dateValue: "2024-01-01", value: "100.5" } + { dateValue: "2024-01-01", value: "100.5" }, ]); ``` @@ -70,15 +78,15 @@ await composedStream.setTaxonomy({ taxonomyItems: [ { childStream: childStreamLocator, - weight: "1.5" - } + weight: "1.5", + }, ], - startDate: "2024-01-01" // Optional + startDate: "2024-01-01", // Optional }); // Get taxonomy const taxonomy = await composedStream.describeTaxonomies({ - latestVersion: true + latestVersion: true, }); ``` @@ -119,5 +127,4 @@ const allStreams = await client.getAllStreams(); const ownerStreams = await client.getAllStreams(ownerAddress); ``` - For comprehensive examples of these APIs in use, please check our [integration tests](../tests/integration). diff --git a/src/contracts-api/stream.ts b/src/contracts-api/stream.ts index 03c5f1e..a2f19e7 100644 --- a/src/contracts-api/stream.ts +++ b/src/contracts-api/stream.ts @@ -36,6 +36,10 @@ export interface StreamRecord { value: string; } +export interface GetIndexChangeInput extends GetRecordInput { + daysInterval: number; +} + export class Stream { protected kwilClient: WebKwil | NodeKwil; protected kwilSigner: KwilSigner; @@ -482,4 +486,33 @@ export class Stream { }; }); } + + /** + * Returns the index change of the stream within the given date range + */ + public async getIndexChange( + input: GetIndexChangeInput, + ): Promise { + const result = await this.call<{ date_value: string; value: string }[]>( + "get_index_change", + [ + ActionInput.fromObject({ + $date_from: input.dateFrom, + $date_to: input.dateTo, + $frozen_at: input.frozenAt, + $base_date: input.baseDate, + $days_interval: input.daysInterval, + }), + ], + ); + + return result + .mapRight((result) => + result.map((row) => ({ + dateValue: row.date_value, + value: row.value, + })), + ) + .throw(); + } } diff --git a/tests/integration/primitiveStream.test.ts b/tests/integration/primitiveStream.test.ts index 2a61e76..1800c82 100644 --- a/tests/integration/primitiveStream.test.ts +++ b/tests/integration/primitiveStream.test.ts @@ -79,5 +79,69 @@ describe.sequential( } }, ); + + testWithDefaultWallet( + "should calculate index changes correctly", + async ({ defaultClient }) => { + // Generate a unique stream ID + const streamId = await StreamId.generate("test-primitive-stream"); + + try { + // Deploy and initialize stream + await defaultClient.deployStream(streamId, "primitive", true); + const primitiveStream = defaultClient.loadPrimitiveStream({ + streamId, + dataProvider: defaultClient.address(), + }); + const initTx = await primitiveStream.initializeStream(); + await defaultClient.waitForTx(initTx.data!.tx_hash!); + + // Insert historical records (2022) + const historicalRecords = [ + { dateValue: "2022-01-01", value: "100" }, + { dateValue: "2022-06-01", value: "120" }, + { dateValue: "2022-12-01", value: "150" }, + ]; + const historicalTx = + await primitiveStream.insertRecords(historicalRecords); + await defaultClient.waitForTx(historicalTx.data!.tx_hash!); + + // Insert current records (2023) + const currentRecords = [ + { dateValue: "2023-01-01", value: "200" }, + { dateValue: "2023-06-01", value: "180" }, + { dateValue: "2023-12-01", value: "240" }, + ]; + const currentTx = await primitiveStream.insertRecords(currentRecords); + await defaultClient.waitForTx(currentTx.data!.tx_hash!); + + // Calculate year-over-year changes + const changes = await primitiveStream.getIndexChange({ + dateFrom: "2023-01-01", + dateTo: "2023-12-31", + daysInterval: 365, + baseDate: "2022-01-01", + }); + + // Verify the changes + expect(changes.length).toBe(3); + + // 2023-01-01 vs 2022-01-01: ((200 - 100) / 100) * 100 = 100% + expect(changes[0].dateValue).toBe("2023-01-01"); + expect(parseFloat(changes[0].value)).toBeCloseTo(100); + + // 2023-06-01 vs 2022-06-01: ((180 - 120) / 120) * 100 = 50% + expect(changes[1].dateValue).toBe("2023-06-01"); + expect(parseFloat(changes[1].value)).toBeCloseTo(50); + + // 2023-12-01 vs 2022-12-01: ((240 - 150) / 150) * 100 = 60% + expect(changes[2].dateValue).toBe("2023-12-01"); + expect(parseFloat(changes[2].value)).toBeCloseTo(60); + } finally { + // Cleanup + await defaultClient.destroyStream(streamId, true); + } + }, + ); }, );