Skip to content

Commit

Permalink
Removed getMetrics for interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaron committed Mar 28, 2024
1 parent 013c08e commit 3ccea8d
Show file tree
Hide file tree
Showing 26 changed files with 41 additions and 213 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,16 +138,9 @@ internal class AvroSamples {
@Test
@Ignore
internal fun signalsOnly() {
class MyPolicy : BasePolicy(prefix = "") {

init {
enableMetrics = true
}
class MyPolicy : BasePolicy() {

override fun act(signals: List<Signal>, account: Account, event: Event): List<Order> {
for (signal in signals) {
record("signal.${signal.asset.symbol}", signal.rating)
}
return emptyList()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ internal class BinanceHistoricFeedTestIT {
@Test
fun test() {
val feed = BinanceHistoricFeed()
assertEquals(1, feed.availableAssets.findBySymbols("BTCUST").size)
val symbol = "BTCUSDT"
assertEquals(1, feed.availableAssets.findBySymbols(symbol).size)

val asset = feed.availableAssets.getBySymbol("BTCUST")
val asset = feed.availableAssets.getBySymbol(symbol)
assertEquals(asset.type, AssetType.CRYPTO)

val tf = Timeframe.past(100.days)
feed.retrieve("BTCBUSD", timeframe = tf)
feed.retrieve(symbol, timeframe = tf)
assertEquals(1, feed.assets.size)

assertThrows<IllegalArgumentException> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ import org.roboquant.common.Logging
import org.roboquant.common.addNotNull
import org.roboquant.common.percent
import org.roboquant.feeds.Event
import org.roboquant.strategies.RecordingStrategy
import org.roboquant.strategies.Signal
import org.roboquant.strategies.Strategy
import smile.data.DataFrame
import smile.regression.DataFrameRegression

Expand All @@ -34,11 +34,10 @@ open class RegressionStrategy(
private val asset: Asset,
private val percentage: Double = 1.percent,
val block: (DataFrame) -> DataFrameRegression
) : RecordingStrategy() {
) : Strategy {

private val logger = Logging.getLogger(this::class)
private var trained = false
private val metricName = "prediction.${asset.symbol.lowercase()}"

private lateinit var model: DataFrameRegression

Expand All @@ -50,7 +49,6 @@ open class RegressionStrategy(
private fun predict(): Double {
val df = featureSet.getPredictData()
val result = model.predict(df).last()
record(metricName, result)
return result
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ internal class MLSamples {
println(model.importance().toList())
model
}
s.recording = true
return s
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,37 @@ import io.questdb.cairo.TableWriter
import io.questdb.griffin.SqlException
import io.questdb.griffin.SqlExecutionContext
import io.questdb.griffin.SqlExecutionContextImpl
import org.roboquant.common.*
import org.roboquant.common.Config
import org.roboquant.common.Logging
import org.roboquant.common.Observation
import org.roboquant.common.TimeSeries
import org.roboquant.loggers.MetricsLogger
import java.nio.file.Files
import java.nio.file.Path
import java.time.Instant
import java.util.concurrent.ConcurrentSkipListSet
import kotlin.collections.Map
import kotlin.collections.Set
import kotlin.collections.component1
import kotlin.collections.component2
import kotlin.collections.iterator
import kotlin.collections.mutableListOf
import kotlin.collections.mutableMapOf
import kotlin.collections.set
import kotlin.collections.setOf
import kotlin.collections.toSet
import kotlin.collections.toSortedSet
import kotlin.io.path.div
import kotlin.io.path.isDirectory

/**
* Log metrics to a QuestDB database
*/
class QuestDBMetricsLogger(dbPath: Path = Config.home / "questdb-metrics" / "db", workers: Int = 1) : MetricsLogger {
class QuestDBMetricsLogger(
dbPath: Path = Config.home / "questdb-metrics" / "db",
workers: Int = 1,
private val partition: String = QuestDBRecorder.NONE
) : MetricsLogger {

private val logger = Logging.getLogger(this::class)
private var engine: CairoEngine
Expand All @@ -43,6 +61,7 @@ class QuestDBMetricsLogger(dbPath: Path = Config.home / "questdb-metrics" / "db"
private val tables = ConcurrentSkipListSet<String>()

init {
require(partition in setOf("YEAR", "MONTH", "DAY", "HOUR", "NONE")) { "invalid partition value" }
if (Files.notExists(dbPath)) {
logger.info { "Creating new database path=$dbPath" }
Files.createDirectories(dbPath)
Expand Down Expand Up @@ -158,7 +177,7 @@ class QuestDBMetricsLogger(dbPath: Path = Config.home / "questdb-metrics" / "db"
|metric SYMBOL,
|value DOUBLE,
|time TIMESTAMP
|), INDEX(metric) timestamp(time)""".trimMargin(),
|), INDEX(metric) timestamp(time) PARTITION BY $partition""".trimMargin(),
)

engine.update("TRUNCATE TABLE '$tableName'")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ internal fun CairoEngine.dropTable(tableName: String) {

internal fun CairoEngine.tableColumns(tableName: String): Set<String> {
val result = mutableSetOf<String>()
query("select column from table_columns('$tableName')") {
query("select \"column\" from table_columns('$tableName')") {
while (hasNext()) {
result.add(record.getStr(0).toString())
}
Expand Down
6 changes: 2 additions & 4 deletions roboquant-ta/src/main/kotlin/org/roboquant/ta/RSIStrategy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import org.roboquant.common.Asset
import org.roboquant.common.PriceSeries
import org.roboquant.common.addAll
import org.roboquant.feeds.Event
import org.roboquant.strategies.RecordingStrategy
import org.roboquant.strategies.Signal
import org.roboquant.strategies.Strategy

/**
* Strategy using the Relative Strength MetadataProvider of an asset to generate signals. RSI measures the magnitude
Expand All @@ -40,7 +40,7 @@ class RSIStrategy(
val highThreshold: Double = 70.0,
private val windowSize: Int = 14,
private val priceType: String = "DEFAULT"
) : RecordingStrategy(prefix = "rsi.") {
) : Strategy {

private val history = mutableMapOf<Asset, PriceSeries>()
private val taLib = TaLib()
Expand All @@ -53,7 +53,6 @@ class RSIStrategy(
}

/**
* @see RecordingStrategy.generate
*/
override fun generate(event: Event): List<Signal> {
history.addAll(event, 1, priceType)
Expand All @@ -63,7 +62,6 @@ class RSIStrategy(
try {
if (data.isFull()) {
val rsi = taLib.rsi(data.toDoubleArray(), windowSize)
record(asset.symbol, rsi)
if (rsi > highThreshold)
result.add(Signal.sell(asset))
else if (rsi < lowThreshold)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ package org.roboquant.ta

import org.roboquant.feeds.Event
import org.roboquant.feeds.PriceBar
import org.roboquant.strategies.RecordingStrategy
import org.roboquant.strategies.Signal
import org.roboquant.strategies.Strategy
import java.lang.Integer.max
Expand All @@ -34,7 +33,7 @@ import java.lang.Integer.max
* If not enough history is available to calculate the indicators, the capacity will be automatically increased until
* it is able to perform the calculations.
*/
class TaLibStrategy(initialCapacity: Int = 1) : RecordingStrategy(recording = true) {
class TaLibStrategy(initialCapacity: Int = 1) : Strategy {

private var sellFn: TaLib.(series: PriceBarSeries) -> Boolean = { false }
private var buyFn: TaLib.(series: PriceBarSeries) -> Boolean = { false }
Expand Down
3 changes: 0 additions & 3 deletions roboquant/src/main/kotlin/org/roboquant/Roboquant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,6 @@ data class Roboquant(
*/
private fun getMetrics(account: Account, event: Event) = buildMap {
for (metric in metrics) putAll(metric.calculate(account, event))
putAll(strategy.getMetrics())
putAll(policy.getMetrics())
putAll(broker.getMetrics())
kotlinLogger.trace { "captured metrics=$size" }
}

Expand Down
6 changes: 0 additions & 6 deletions roboquant/src/main/kotlin/org/roboquant/brokers/Broker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ interface Broker {
*/
fun place(orders: List<Order>)

/**
* This method will be invoked at each step in a run and provides the broker with the opportunity to
* provide additional metrics. The default implementation returns an empty map.
*/
fun getMetrics(): Map<String, Double> = emptyMap()

/**
* Reset the state of the component to its initial state. The default implementation is to take no action.
*/
Expand Down
2 changes: 1 addition & 1 deletion roboquant/src/main/kotlin/org/roboquant/common/Logging.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ object Logging {
/**
* Logger class that extends a SLF4J logger and allows for Kotlin idiomatic usage patterns
*/
class Logger(private val slf4jLogger: org.slf4j.Logger) : org.slf4j.Logger by slf4jLogger {
class Logger(@Suppress("unused") private val slf4jLogger: org.slf4j.Logger) : org.slf4j.Logger by slf4jLogger {

/**
* @see org.slf4j.Logger.trace
Expand Down
38 changes: 1 addition & 37 deletions roboquant/src/main/kotlin/org/roboquant/policies/BasePolicy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,51 +22,15 @@ import org.roboquant.common.Asset
import org.roboquant.orders.MarketOrder
import org.roboquant.orders.Order
import org.roboquant.orders.createCancelOrders
import kotlin.collections.set

/**
* Contains a number of utility methods that are useful when implementing a new policy.
*
* For example, how to deal with conflicting signals or how to handle amounts in a multi-currency environment. It
* also contains a simple method to record metrics.
*
* @property enableMetrics should metrics be exposed, default is false
*/
abstract class BasePolicy(private val prefix: String = "policy.") : Policy {

/**
* should metrics be exposed, default is false
*/
var enableMetrics: Boolean = false

private val metrics = mutableMapOf<String, Double>()

/**
* Record a metric
*
* @param key The name of the metric
* @param value The value of the metric
*/
protected fun record(key: String, value: Number) {
if (!enableMetrics) return
metrics["$prefix$key"] = value.toDouble()
}

/**
* Return any recorded metrics
*/
override fun getMetrics(): Map<String, Double> {
val result = metrics.toMap()
metrics.clear()
return result
}

/**
* Reset the state, including any recorded metrics.
*/
override fun reset() {
metrics.clear()
}
abstract class BasePolicy : Policy {

/**
* Create a new market-order to close an open position for an [asset] and cancel-orders any open orders for
Expand Down
16 changes: 0 additions & 16 deletions roboquant/src/main/kotlin/org/roboquant/policies/FlexPolicy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ open class FlexPolicyConfig(
* For example, this policy will create [MarketOrder]s by default, but this can be changed by overwriting
* the [createOrder] method in a subclass.
*
* When [enableMetrics] is set to true, it will record the number of `actions`, `signals` and `orders`.
*
* @constructor Create a new instance of a FlexPolicy
* @param configure additional configuration parameters
Expand Down Expand Up @@ -255,20 +254,6 @@ open class FlexPolicy(
*/
private operator fun Collection<Order>.contains(asset: Asset) = any { it.asset == asset }

/**
* Record basic metrics: `actions`, `signals`, `orders.new`, `orders.open`,
* `positions` and `buyingpower`.
*
* The main purpose is to better understand when the policy is not behaving as expected.
*/
open fun record(orders: List<Order>, signals: List<Signal>, event: Event, account: Account) {
record("actions", event.items.size)
record("signals", signals.size)
record("orders.new", orders.size)
record("orders.open", account.openOrders.size)
record("positions", account.positions.size)
record("buyingpower", account.buyingPower.value)
}

/**
* @see Policy.act
Expand Down Expand Up @@ -327,7 +312,6 @@ open class FlexPolicy(

}
}
if (enableMetrics) record(orders, signals, event, account)
return orders
}
}
7 changes: 0 additions & 7 deletions roboquant/src/main/kotlin/org/roboquant/policies/Policy.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,6 @@ interface Policy {
*/
fun act(signals: List<Signal>, account: Account, event: Event): List<Order>

/**
* This will be invoked at each step in a run and provides the implementation with the opportunity to log additional
* information. The default implementation is to return an empty map.
*
* This map does not mutate after it has been returned by this method.
*/
fun getMetrics(): Map<String, Double> = emptyMap()

/**
* Reset the state of the component to its initial state. The default implementation is to take no action.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,4 @@ open class CombinedStrategy(val strategies: Collection<Strategy>) : Strategy {
for (strategy in strategies) strategy.reset()
}

override fun getMetrics(): Map<String, Double> {
val result = mutableMapOf<String, Double>()
strategies.forEach { result += it.getMetrics() }
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class EMAStrategy(
smoothing: Double = 2.0,
private val minEvents: Int = slowPeriod,
priceType: String = "DEFAULT"
) : PriceStrategy(priceType = priceType, prefix = "strategy.ema.") {
) : PriceStrategy(priceType = priceType) {

private val fast = 1.0 - (smoothing / (fastPeriod + 1))
private val slow = 1.0 - (smoothing / (slowPeriod + 1))
Expand Down Expand Up @@ -105,11 +105,6 @@ class EMAStrategy(
if (calculator.isReady()) {
val newDirection = calculator.getDirection()

if (recording) {
record("${asset.symbol}.fast", calculator.emaFast)
record("${asset.symbol}.slow", calculator.emaSlow)
}

if (oldDirection != newDirection) {
val rating = if (newDirection) 1.0 else -1.0
return Signal(asset, rating)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import org.roboquant.feeds.Event
abstract class HistoricPriceStrategy(
private val period: Int,
private val priceType: String = "DEFAULT",
) : RecordingStrategy() {
) : Strategy {

/**
* Contain the history of all assets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ import java.time.Instant
* Base class that can be extended by strategies that are only interested in a single price for an asset and not
* other types of actions.
*/
abstract class PriceStrategy(private val priceType: String = "DEFAULT", prefix: String = "strategy.") :
RecordingStrategy(prefix) {
abstract class PriceStrategy(private val priceType: String = "DEFAULT") :
Strategy {

override fun generate(event: Event): List<Signal> {
val signals = mutableListOf<Signal>()
Expand Down
Loading

0 comments on commit 3ccea8d

Please sign in to comment.