implement market overview endpoint

This commit is contained in:
2026-06-22 12:31:31 +02:00
parent e22497527c
commit 922cee3c23
5 changed files with 140 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
package com.frankenergie.smartasset.controller
import com.frankenergie.smartasset.model.MarketOverviewResponse
import com.frankenergie.smartasset.model.OrderUpdateRequest
import com.frankenergie.smartasset.model.OrderUpdateResponse
import com.frankenergie.smartasset.service.OrderBookService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class MarketOverviewController(private val orderBookService: OrderBookService) {
@GetMapping("/market/overview")
fun getMarketOverview(): ResponseEntity<MarketOverviewResponse> {
val response = orderBookService.getMarketOverview()
return ResponseEntity.ok(response)
}
}

View File

@@ -0,0 +1,12 @@
package com.frankenergie.smartasset.model
import com.fasterxml.jackson.annotation.JsonProperty
import java.math.BigDecimal
import java.time.LocalDateTime
data class QuarterOverview(
@JsonProperty("delivery_start_time") val deliveryStartTime: LocalDateTime,
@JsonProperty("delivery_end_time") val deliveryEndTime: LocalDateTime,
@JsonProperty("highest_buy_price") val highestBuyPrice: BigDecimal?,
@JsonProperty("lowest_sell_price") val lowestSellPrice: BigDecimal?
)

View File

@@ -0,0 +1,9 @@
package com.frankenergie.smartasset.model
import com.fasterxml.jackson.annotation.JsonProperty
import java.math.BigDecimal
import java.time.LocalDateTime
data class MarketOverviewResponse(
@JsonProperty("quarters") val quarters: List<QuarterOverview>
)

View File

@@ -2,8 +2,10 @@ package com.frankenergie.smartasset.service
import com.frankenergie.smartasset.domain.OrderBook import com.frankenergie.smartasset.domain.OrderBook
import com.frankenergie.smartasset.model.DeliveryPeriod import com.frankenergie.smartasset.model.DeliveryPeriod
import com.frankenergie.smartasset.model.MarketOverviewResponse
import com.frankenergie.smartasset.model.OrderUpdateRequest import com.frankenergie.smartasset.model.OrderUpdateRequest
import com.frankenergie.smartasset.model.OrderUpdateResponse import com.frankenergie.smartasset.model.OrderUpdateResponse
import com.frankenergie.smartasset.model.QuarterOverview
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.Instant import java.time.Instant
import java.util.UUID import java.util.UUID
@@ -30,5 +32,19 @@ class OrderBookService {
return OrderUpdateResponse(orderId = orderId, status = status, timestamp = Instant.now()) return OrderUpdateResponse(orderId = orderId, status = status, timestamp = Instant.now())
} }
fun getMarketOverview(): MarketOverviewResponse {
val quarters = orderBook.getAllPeriods()
.sortedBy { it.startTime }
.map { period ->
QuarterOverview(
deliveryStartTime = period.startTime,
deliveryEndTime = period.endTime,
highestBuyPrice = orderBook.getBestBid(period)?.price,
lowestSellPrice = orderBook.getBestAsk(period)?.price
)
}
return MarketOverviewResponse(quarters)
}
fun getOrderBook(): OrderBook = orderBook fun getOrderBook(): OrderBook = orderBook
} }

View File

@@ -0,0 +1,82 @@
package com.frankenergie.smartasset.controller
import com.fasterxml.jackson.databind.ObjectMapper
import com.frankenergie.smartasset.model.OrderSide
import com.frankenergie.smartasset.model.OrderUpdateRequest
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.http.MediaType
import org.springframework.test.web.servlet.MockMvc
import org.springframework.test.web.servlet.get
import org.springframework.test.web.servlet.post
import java.math.BigDecimal
import java.time.LocalDateTime
@SpringBootTest
@AutoConfigureMockMvc
class MarketOverviewControllerTest {
@Autowired
private lateinit var mockMvc: MockMvc
@Autowired
private lateinit var objectMapper: ObjectMapper
@Test
fun `GET market overview returns empty list when no orders`() {
mockMvc.get("/api/market/overview")
.andExpect {
status { isOk() }
jsonPath("$.quarters") { isArray() }
}
}
@Test
fun `GET market overview returns quarters with best prices`() {
// Add a buy order
val buyRequest = OrderUpdateRequest(
deliveryStartTime = LocalDateTime.of(2024, 6, 15, 10, 0),
deliveryEndTime = LocalDateTime.of(2024, 6, 15, 10, 15),
orderSide = OrderSide.BUY,
quantity = BigDecimal("10"),
price = BigDecimal("50.00")
)
mockMvc.post("/api/orderupdate") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(buyRequest)
}
// Add a higher buy order for same quarter
val higherBuyRequest = buyRequest.copy(price = BigDecimal("52.00"))
mockMvc.post("/api/orderupdate") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(higherBuyRequest)
}
// Add a sell order
val sellRequest = buyRequest.copy(orderSide = OrderSide.SELL, price = BigDecimal("55.00"))
mockMvc.post("/api/orderupdate") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(sellRequest)
}
// Add a lower sell order
val lowerSellRequest = buyRequest.copy(orderSide = OrderSide.SELL, price = BigDecimal("53.00"))
mockMvc.post("/api/orderupdate") {
contentType = MediaType.APPLICATION_JSON
content = objectMapper.writeValueAsString(lowerSellRequest)
}
mockMvc.get("/api/market/overview")
.andExpect {
status { isOk() }
jsonPath("$.quarters.length()") { value(1) }
jsonPath("$.quarters[0].delivery_start_time") { exists() }
jsonPath("$.quarters[0].delivery_end_time") { exists() }
jsonPath("$.quarters[0].highest_buy_price") { value(52.00) }
jsonPath("$.quarters[0].lowest_sell_price") { value(53.00) }
}
}
}