implement market overview endpoint
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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?
|
||||||
|
)
|
||||||
@@ -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>
|
||||||
|
)
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user