From c6ed05c48bbaf4eef11c867079a862e75fa9cd13 Mon Sep 17 00:00:00 2001 From: Szkkhh <2077696163@qq.com> Date: Fri, 16 Jan 2026 20:03:08 +0800 Subject: [PATCH 1/3] #78 Add new API integration: /key-metrics-ttm-bulk --- docs/README.md | 2 +- .../fmp4j/clients/FmpBulkClient.java | 8 +++ .../services/FmpBulkKeyMetricTtmService.java | 32 ++++++++++ .../FmpBulkKeyMetricTtmServiceTest.java | 63 +++++++++++++++++++ .../stable/key-metrics-ttm-bulk/.csv | 3 + 5 files changed, 107 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmService.java create mode 100644 src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java create mode 100644 src/testFixtures/resources/stable/key-metrics-ttm-bulk/.csv diff --git a/docs/README.md b/docs/README.md index 1067206..7495554 100644 --- a/docs/README.md +++ b/docs/README.md @@ -511,7 +511,7 @@ Copied from [/developer/docs/stable#bulk](https://site.financialmodelingprep.com | ❌️ | - | `/price-target-summary-bulk` | | ❌️ | - | `/etf-holder-bulk` | | ❌️ | - | `/upgrades-downgrades-consensus-bulk` | -| ❌️ | - | `/key-metrics-ttm-bulk` | +| ✅️ | - | `/key-metrics-ttm-bulk` | | ❌️ | - | `/ratios-ttm-bulk` | | ❌️ | - | `/ratios-ttm-bulk` | | ❌️ | - | `/peers-bulk` | diff --git a/src/main/java/com/marketdataapi/fmp4j/clients/FmpBulkClient.java b/src/main/java/com/marketdataapi/fmp4j/clients/FmpBulkClient.java index 655f8e7..1528417 100644 --- a/src/main/java/com/marketdataapi/fmp4j/clients/FmpBulkClient.java +++ b/src/main/java/com/marketdataapi/fmp4j/clients/FmpBulkClient.java @@ -9,10 +9,12 @@ import com.marketdataapi.fmp4j.models.FmpCashFlowStatement; import com.marketdataapi.fmp4j.models.FmpCashFlowStatementGrowth; import com.marketdataapi.fmp4j.models.FmpCompanies; +import com.marketdataapi.fmp4j.models.FmpKeyMetricTtm; import com.marketdataapi.fmp4j.services.FmpBulkBalanceSheetStatementService; import com.marketdataapi.fmp4j.services.FmpBulkCashFlowStatementGrowthService; import com.marketdataapi.fmp4j.services.FmpBulkCashFlowStatementService; import com.marketdataapi.fmp4j.services.FmpBulkCompaniesService; +import com.marketdataapi.fmp4j.services.FmpBulkKeyMetricTtmService; import com.marketdataapi.fmp4j.services.FmpService; import com.marketdataapi.fmp4j.types.FmpPart; import com.marketdataapi.fmp4j.types.FmpPeriod; @@ -26,6 +28,7 @@ public class FmpBulkClient { protected final FmpService fmpBulkBalanceSheetService; protected final FmpService fmpBulkCashFlowService; protected final FmpService fmpBulkCashFlowStatementGrowthService; + protected final FmpService fmpBulkKeyMetricTtmService; public FmpBulkClient(FmpConfig fmpConfig, FmpHttpClient fmpHttpClient) { this.fmpBulkCompaniesService = new FmpBulkCompaniesService(fmpConfig, fmpHttpClient); @@ -33,6 +36,7 @@ public FmpBulkClient(FmpConfig fmpConfig, FmpHttpClient fmpHttpClient) { this.fmpBulkCashFlowService = new FmpBulkCashFlowStatementService(fmpConfig, fmpHttpClient); this.fmpBulkCashFlowStatementGrowthService = new FmpBulkCashFlowStatementGrowthService(fmpConfig, fmpHttpClient); + this.fmpBulkKeyMetricTtmService = new FmpBulkKeyMetricTtmService(fmpConfig, fmpHttpClient); } public synchronized List companies(FmpPart part) { @@ -57,4 +61,8 @@ public synchronized List cashFlowStatementGrowth(Fmp fmpBulkCashFlowStatementGrowthService.param(PARAM_PERIOD, period); return fmpBulkCashFlowStatementGrowthService.download(); } + + public synchronized List keyMetricsTtm() { + return fmpBulkKeyMetricTtmService.download(); + } } diff --git a/src/main/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmService.java b/src/main/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmService.java new file mode 100644 index 0000000..fdb6cf6 --- /dev/null +++ b/src/main/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmService.java @@ -0,0 +1,32 @@ +package com.marketdataapi.fmp4j.services; + +import com.marketdataapi.fmp4j.cfg.FmpConfig; +import com.marketdataapi.fmp4j.http.FmpHttpClient; +import com.marketdataapi.fmp4j.models.FmpKeyMetricTtm; +import java.util.Map; + +public class FmpBulkKeyMetricTtmService extends FmpService { + public FmpBulkKeyMetricTtmService(FmpConfig cfg, FmpHttpClient http) { + super(cfg, http, FmpKeyMetricTtm.class); + } + + @Override + protected String relativeUrl() { + return "/key-metrics-ttm-bulk"; + } + + @Override + protected Map> requiredParams() { + return Map.of(); + } + + @Override + protected Map> optionalParams() { + return Map.of(); + } + + @Override + protected Map headers() { + return Map.of("Content-Type", "text/csv"); + } +} diff --git a/src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java b/src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java new file mode 100644 index 0000000..5db3cf7 --- /dev/null +++ b/src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java @@ -0,0 +1,63 @@ +package com.marketdataapi.fmp4j.services; + +import static com.marketdataapi.fmp4j.TestUtils.assertAllFieldsNonNull; +import static com.marketdataapi.fmp4j.TestUtils.testResource; +import static java.util.stream.IntStream.range; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.marketdataapi.fmp4j.models.FmpKeyMetricTtm; +import java.util.Map; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class FmpBulkKeyMetricTtmServiceTest extends HttpTest { + private FmpService service; + + @BeforeEach + void setup() { + service = new FmpBulkKeyMetricTtmService(config, client); + } + + @Test + void relative_url() { + // given // when + var relativeUrl = service.relativeUrl(); + + // then + assertEquals("/key-metrics-ttm-bulk", relativeUrl); + } + + @Test + void required_params() { + // given // when + var params = service.requiredParams(); + + // then + assertEquals(Map.of(), params); + } + + @Test + void optional_params() { + // given // when + var params = service.optionalParams(); + + // then + assertEquals(Map.of(), params); + } + + @Test + void successful_download() { + // given + httpStub.configureResponse() + .body(testResource("stable/key-metrics-ttm-bulk/.csv")) + .statusCode(200) + .apply(); + + // when + var result = service.download(); + + // then + assertEquals(2, result.size()); + range(0, result.size()).forEach(i -> assertAllFieldsNonNull(result.get(i))); + } +} diff --git a/src/testFixtures/resources/stable/key-metrics-ttm-bulk/.csv b/src/testFixtures/resources/stable/key-metrics-ttm-bulk/.csv new file mode 100644 index 0000000..08b3971 --- /dev/null +++ b/src/testFixtures/resources/stable/key-metrics-ttm-bulk/.csv @@ -0,0 +1,3 @@ +"symbol","marketCap","enterpriseValueTTM","evToSalesTTM","evToOperatingCashFlowTTM","evToFreeCashFlowTTM","evToEBITDATTM","netDebtToEBITDATTM","currentRatioTTM","incomeQualityTTM","grahamNumberTTM","grahamNetNetTTM","taxBurdenTTM","interestBurdenTTM","workingCapitalTTM","investedCapitalTTM","returnOnAssetsTTM","operatingReturnOnAssetsTTM","returnOnTangibleAssetsTTM","returnOnEquityTTM","returnOnInvestedCapitalTTM","returnOnCapitalEmployedTTM","earningsYieldTTM","freeCashFlowYieldTTM","capexToOperatingCashFlowTTM","capexToDepreciationTTM","capexToRevenueTTM","salesGeneralAndAdministrativeToRevenueTTM","researchAndDevelopementToRevenueTTM","stockBasedCompensationToRevenueTTM","intangiblesToTotalAssetsTTM","averageReceivablesTTM","averagePayablesTTM","averageInventoryTTM","daysOfSalesOutstandingTTM","daysOfPayablesOutstandingTTM","daysOfInventoryOutstandingTTM","operatingCycleTTM","cashConversionCycleTTM","freeCashFlowToEquityTTM","freeCashFlowToFirmTTM","tangibleAssetValueTTM","netCurrentAssetValueTTM" +"AAPL",2800000000000,2700000000000,7.5,12.3,15.2,20.5,0.8,1.2,1.1,45.2,-5.3,0.85,0.95,50000000000,1500000000000,0.25,0.30,0.28,0.45,0.35,0.40,0.12,0.08,0.15,0.5,0.03,0.05,0.08,0.01,0.02,30000000000,25000000000,15000000000,12,15,20,32,17,80000000000,90000000000,200000000000,180000000000 +"GOOGL",2000000000000,1900000000000,6.8,15.1,18.5,22.3,0.6,1.3,1.2,38.7,-8.2,0.88,0.92,60000000000,1200000000000,0.22,0.28,0.25,0.38,0.30,0.35,0.10,0.07,0.12,0.4,0.02,0.04,0.07,0.01,0.01,25000000000,20000000000,10000000000,10,12,18,28,16,70000000000,80000000000,150000000000,140000000000 From 4e72660fb8491c24a5bcb0f0a7f6becd2eff2c14 Mon Sep 17 00:00:00 2001 From: Szkkhh <2077696163@qq.com> Date: Fri, 16 Jan 2026 20:55:16 +0800 Subject: [PATCH 2/3] #78 add tests for FmpClient --- .../com/marketdataapi/fmp4j/FmpClientTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java b/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java index 89e771c..cb5dc3e 100644 --- a/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java +++ b/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java @@ -757,6 +757,20 @@ void keyMetrics(String p) { assertValidResult(result, limit.value(), FmpKeyMetric.class); } + @Test + void key_metrics_ttm_bulk() { + // given + var endpoint = "key-metrics-ttm-bulk"; + var file = format("stable/%s/.csv", endpoint); + + // when + mockHttpGetFromFile(file); + var result = fmpClient.bulk().keyMetricsTtm(); + + // then + assertValidResult(result, 2, FmpKeyMetricTtm.class); + } + @Test void key_metrics_ttm() { // given From ec2e179665f5818d74302b323154d110beffc833 Mon Sep 17 00:00:00 2001 From: Szkkhh <2077696163@qq.com> Date: Sat, 17 Jan 2026 15:20:39 +0800 Subject: [PATCH 3/3] #78 Rename the CSV file --- src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java | 2 +- .../fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java | 2 +- .../resources/stable/key-metrics-ttm-bulk/{.csv => excerpt.csv} | 0 3 files changed, 2 insertions(+), 2 deletions(-) rename src/testFixtures/resources/stable/key-metrics-ttm-bulk/{.csv => excerpt.csv} (100%) diff --git a/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java b/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java index cb5dc3e..6142ca4 100644 --- a/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java +++ b/src/test/java/com/marketdataapi/fmp4j/FmpClientTest.java @@ -761,7 +761,7 @@ void keyMetrics(String p) { void key_metrics_ttm_bulk() { // given var endpoint = "key-metrics-ttm-bulk"; - var file = format("stable/%s/.csv", endpoint); + var file = format("stable/%s/excerpt.csv", endpoint); // when mockHttpGetFromFile(file); diff --git a/src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java b/src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java index 5db3cf7..f61a5f7 100644 --- a/src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java +++ b/src/test/java/com/marketdataapi/fmp4j/services/FmpBulkKeyMetricTtmServiceTest.java @@ -49,7 +49,7 @@ void optional_params() { void successful_download() { // given httpStub.configureResponse() - .body(testResource("stable/key-metrics-ttm-bulk/.csv")) + .body(testResource("stable/key-metrics-ttm-bulk/excerpt.csv")) .statusCode(200) .apply(); diff --git a/src/testFixtures/resources/stable/key-metrics-ttm-bulk/.csv b/src/testFixtures/resources/stable/key-metrics-ttm-bulk/excerpt.csv similarity index 100% rename from src/testFixtures/resources/stable/key-metrics-ttm-bulk/.csv rename to src/testFixtures/resources/stable/key-metrics-ttm-bulk/excerpt.csv