diff --git a/roboquant/src/main/kotlin/org/roboquant/http/WebServer.kt b/roboquant/src/main/kotlin/org/roboquant/http/WebServer.kt index b7ec0c8f4..c33a3cb99 100644 --- a/roboquant/src/main/kotlin/org/roboquant/http/WebServer.kt +++ b/roboquant/src/main/kotlin/org/roboquant/http/WebServer.kt @@ -26,10 +26,26 @@ import org.roboquant.common.Timeframe import org.roboquant.feeds.Event import org.roboquant.feeds.Feed import org.roboquant.metrics.Metric +import org.roboquant.orders.Order import org.roboquant.orders.lines +import org.roboquant.policies.Policy +import org.roboquant.strategies.Signal import java.io.OutputStream import java.net.InetSocketAddress +/** + * Allows a policy to be paused, aka don't generate orders. + */ +private class PausablePolicy(val policy: Policy, var pause: Boolean = false) : Policy by policy { + + override fun act(signals: List, account: Account, event: Event): List { + // Still invoke the policy so any state can be updated if required. + val orders = policy.act(signals, account, event) + return if (pause) emptyList() else orders + } + +} + /** * Metric used to capture basic information of a run that is displayed on the web pages. */ @@ -177,24 +193,53 @@ class WebServer(port: Int = 8000, username: String, password: String) { return String.format(template, result) } + private fun pause(run: String) { + val info = runs.getValue(run) + val policy = info.roboquant.policy as PausablePolicy + policy.pause = true + } + + private fun resume(run: String) { + val info = runs.getValue(run) + val policy = info.roboquant.policy as PausablePolicy + policy.pause = false + } + + + private fun overviewRuns(query: String): String { + println(query) + if (query.isNotBlank()) { + val (action, run) = query.split('=') + when (action) { + "pause" -> pause(run) + "resume" -> resume(run) + } + } - private fun overviewRuns(): String { val result = StringBuilder() result.append( """ - +
RunTimeframeOrdersLast UpdateActions
""".trimIndent() ) for ((run, info) in runs) { val account = info.metric.account val orders = if (account != null) account.closedOrders.size + account.openOrders.size else 0 + val paused = (info.roboquant.policy as PausablePolicy).pause + val state = if (paused) "paused" else "running" result.append("") result.append("") result.append("") + result.append("") result.append("") result.append("") - result.append("") + result.append(""" + """) result.append("") } result.append("
RunTimeframeStateOrdersLast UpdateActions
$run${info.timeframe}$state$orders${account?.lastUpdate}Details + Details + Pause + Resume +
") @@ -210,9 +255,8 @@ class WebServer(port: Int = 8000, username: String, password: String) { val path = it.requestURI.path val content = when { path.startsWith("/runs/") -> detailsRun(path.split('/').last()) - else -> overviewRuns() + else -> overviewRuns(it.requestURI.query ?: "") } - println(it.requestURI.path) val response = content.encodeToByteArray() it.sendResponseHeaders(200, response.size.toLong()) val os: OutputStream = it.responseBody @@ -240,7 +284,7 @@ class WebServer(port: Int = 8000, username: String, password: String) { val metric = WebMetric() val run = "run-${run++}" - val rq = roboquant.copy(metrics = roboquant.metrics + metric) + val rq = roboquant.copy(metrics = roboquant.metrics + metric, policy = PausablePolicy(roboquant.policy)) runs[run] = RunInfo(metric, rq, feed, timeframe, warmup) rq.runAsync(feed, timeframe, warmup, run) }