Skip to content

Commit

Permalink
Cleanup back test code
Browse files Browse the repository at this point in the history
  • Loading branch information
jbaron committed Aug 1, 2023
1 parent c1c0677 commit 13c2db8
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 21 deletions.
8 changes: 3 additions & 5 deletions roboquant/src/main/kotlin/org/roboquant/backtest/Backtest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Backtest(val feed: Feed, val roboquant: Roboquant) {
* including the warmup period.
*/
fun singleRun(timeframe: Timeframe = feed.timeframe, warmup: TimeSpan = TimeSpan.ZERO) {
roboquant.run(feed, timeframe, name = "run-$timeframe", warmup)
roboquant.run(feed, timeframe, "run-$timeframe", warmup)
}

/**
Expand All @@ -56,8 +56,7 @@ class Backtest(val feed: Feed, val roboquant: Roboquant) {
) {
require(feed.timeframe.isFinite()) { "feed needs a finite timeframe" }
feed.timeframe.split(period, warmup).forEach {
roboquant.run(feed, it, name = "run-$it", warmup)
roboquant.reset(false)
roboquant.run(feed, it, "run-$it", warmup)
}
}

Expand All @@ -74,8 +73,7 @@ class Backtest(val feed: Feed, val roboquant: Roboquant) {
) {
require(feed.timeframe.isFinite()) { "feed needs a finite timeframe" }
feed.timeframe.sample(period, samples).forEach {
roboquant.run(feed, it, name = "run-$it", warmup)
roboquant.reset(false)
roboquant.run(feed, it, "run-$it", warmup)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ open class Optimizer(
val rq = getRoboquant(params).copy(logger = getTrainLogger())
require(rq.broker is SimBroker) { "Only a SimBroker can be used for back testing" }
val name = "train-${run++}"
rq.runAsync(feed, tf, name = name, warmup)
rq.runAsync(feed, tf, name, warmup)
val s = score.calculate(rq, name, tf)
val result = RunResult(params, s, tf, name)
results.add(result)
Expand Down
4 changes: 2 additions & 2 deletions roboquant/src/main/kotlin/org/roboquant/backtest/Score.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ fun interface Score {
val broker = roboquant.broker as SimBroker
val account = broker.account
val startDeposit = broker.initialDeposit.convert(account.baseCurrency, timeframe.start).value
val percentage = (account.equityAmount.value - startDeposit) / startDeposit
val percentage = account.equityAmount.value/startDeposit - 1.0
return timeframe.annualize(percentage)
}
}
Expand Down Expand Up @@ -73,7 +73,7 @@ class MetricScore(private val metricName: String, private val reduce: (TimeSerie
fun min(ts: TimeSeries) = ts.values.max()
fun annualized(ts: TimeSeries): Double {
if (ts.size < 2) return Double.NaN
val perc = (ts.values.last() - ts.values.first()) / ts.values.first()
val perc = ts.values.last()/ts.values.first() - 1.0
return ts.timeframe.annualize(perc)
}

Expand Down
10 changes: 8 additions & 2 deletions roboquant/src/main/kotlin/org/roboquant/backtest/SearchSpace.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package org.roboquant.backtest


/**
* Interface for all types of search spaces
* Interface for all types of search spaces.
* A search space contains the parameters that define the space as well as how these parameters are accessed,
* for example, grid or random.
*/
interface SearchSpace : Iterable<Params> {

Expand All @@ -34,7 +36,8 @@ interface SearchSpace : Iterable<Params> {
fun update(params: Params, score: Double) {}

/**
* Returns the total number of parameter combinations found in this search space
* Returns the total number of parameter combinations in this search space.
* This will determine how many back tests are required to find the optimum parameter combination.
*/
val size: Int

Expand Down Expand Up @@ -152,6 +155,9 @@ class GridSearch : SearchSpace {
permutations
}

/**
* Add a parameter [name] with [values] to the search space
*/
fun add(name: String, values: Iterable<Any>) {
params[name] = values.toList()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class ParallelJobs {
}

/**
* Number of available jobs.
* Number of instantiated jobs.
*/
val size
get() = jobs.size
Expand Down
8 changes: 5 additions & 3 deletions roboquant/src/main/kotlin/org/roboquant/common/extensions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,14 @@ fun String.toCurrencyPair(): Pair<Currency, Currency>? {
}

/**
* Extension to use sumOf for [Amount] values. This implementation has an optimized path in case the sum is over
* amounts of a single currency.
* Extension to use sumOf for [Amount] values.
* The result is of type [Wallet], even if summing over a single currency
*
* This implementation has an optimized path in case the sum is over amounts of a single currency.
*
* Example:
* ```
* val realizedPNL = account.trades.sumOf { it.pnl }
* val totalPNL = account.trades.sumOf { it.pnl }
* ```
*/
inline fun <T> Collection<T>.sumOf(
Expand Down
28 changes: 23 additions & 5 deletions roboquant/src/test/kotlin/org/roboquant/backtest/BacktestTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,41 @@ import org.junit.jupiter.api.Test
import org.roboquant.Roboquant
import org.roboquant.common.months
import org.roboquant.feeds.RandomWalkFeed
import org.roboquant.loggers.SilentLogger
import org.roboquant.loggers.MemoryLogger
import org.roboquant.metrics.AccountMetric
import org.roboquant.strategies.EMAStrategy
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class BacktestTest {

val feed = RandomWalkFeed.lastYears(2)
val rq = Roboquant(EMAStrategy.PERIODS_5_15, logger = SilentLogger())
private val feed = RandomWalkFeed.lastYears(2)

@Test
fun runs() {
fun single() {
val rq = Roboquant(EMAStrategy.PERIODS_5_15, AccountMetric(), logger = MemoryLogger(false))
val bt = Backtest(feed, rq)
bt.singleRun()
val data =rq.logger.getMetric("account.equity")
assertEquals(1, data.size)
}

@Test
fun wf() {
val rq = Roboquant(EMAStrategy.PERIODS_5_15, AccountMetric(), logger = MemoryLogger(false))
val bt = Backtest(feed, rq)
bt.walkForward(6.months)
val data = rq.logger.getMetric("account.equity")
assertTrue(data.size >= 3)
}

@Test
fun mc() {
val rq = Roboquant(EMAStrategy.PERIODS_5_15, AccountMetric(), logger = MemoryLogger(false))
val bt = Backtest(feed, rq)
bt.monteCarlo(3.months, 50)

val data = rq.logger.getMetric("account.equity")
assertEquals(50, data.size)
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,7 @@ class OptimizerTest {

val feed = RandomWalkFeed.lastYears(3, nAssets = 2)
val r2 = opt.walkForward(feed, 9.months, 3.months, 0.months, false)
println(r2)

assertTrue(r2.isNotEmpty())
}

@Test
Expand Down

0 comments on commit 13c2db8

Please sign in to comment.