ConvexPi

SDKs — Python, R & Julia

Write quant strategies and live trading agents in the language you think in. The same hidden-holdout engine scores all three, so the same idea earns the same out-of-sample result in any language — you never lose points for your choice of tongue.

One contract, everywhere

A Lab strategy is a functionon_day(day, features, prices, portfolio)that returns a vector of target weights (one per stock). Python wraps it in aMyStrategy(Strategy) class; R and Julia define the function directly. The grader runs it over a hidden holdout and ranks you by out-of-sample Sharpe.

Fit and submit — in each language

Python

· install from PyPI
pip install convexpi-lab

from convexpi.lab import SyntheticMarket, Strategy, submit
import numpy as np

m = SyntheticMarket(seed=42)              # the exact market the grader uses
X = m.features("train"); px = m.prices("train")

class MyStrategy(Strategy):
    def on_day(self, day, features, prices, portfolio):
        s = np.nan_to_num(features["mom_1m"])
        g = np.abs(s).sum()
        return s / g if g > 0 else s      # target weights, gross 1

submit(MyStrategy, competition="demo-fall-2026", name="my-momentum")

R

· install from GitHub
# install.packages("remotes"); remotes::install_github("convexpi/convexpi-r")
library(convexpi)

m <- synthetic_market("train")            # the exact market the grader uses

code <- 'on_day <- function(day, features, prices, portfolio) {
  s <- features[["mom_1m"]]; s[!is.finite(s)] <- 0
  g <- sum(abs(s)); if (g > 0) s / g else s   # target weights, gross 1
}'

Sys.setenv(CONVEXPI_API_KEY = "cpk_...")   # from /settings/api-keys
submit("my-momentum", code)

Julia

· install from Git URL
using Pkg; Pkg.add(url = "https://github.com/convexpi/ConvexPi.jl")
using ConvexPi

m = synthetic_market("train")             # the exact market the grader uses

code = """
function on_day(day, features, prices, portfolio)
    s = copy(features["mom_1m"]); s[.!isfinite.(s)] .= 0.0
    g = sum(abs.(s)); return g > 0 ? s ./ g : s   # target weights, gross 1
end
"""

ENV["CONVEXPI_API_KEY"] = "cpk_..."       # from /settings/api-keys
submit("my-momentum", code)

Live Arena agents

The same three languages trade the live limit-order book. Your agent is a functionon_tick(state) returning orders; in Python it's a RemoteAgent subclass, in R and Julia a function passed to run_agent().

# R
naive_mm <- function(state) {
  if (is.null(state$best_bid)) return(list())
  mid <- (state$best_bid + state$best_ask) %/% 2
  list(arena_limit("buy", mid - 5, 10), arena_limit("sell", mid + 5, 10))
}
run_agent(naive_mm, agent_id = "mm_demo", max_ticks = 200)

# Julia
naive_mm(state) = state.best_bid === nothing ? Dict[] :
    [arena_limit("buy", (state.best_bid + state.best_ask) ÷ 2 - 5, 10),
     arena_limit("sell", (state.best_bid + state.best_ask) ÷ 2 + 5, 10)]
run_agent(naive_mm; agent_id = "mm_demo", max_ticks = 200)

What each SDK gives you

PythonRJulia
Market data — synthetic_market()via reticulatevia PyCall
Lab strategies (on_day, graded)
Live Arena agents (WebSocket)
Simulators — finmlsimbridgebridge
Install fromPyPIGitHub (R-universe soon)Git URL (General soon)

R and Julia get the market and simulators by bridging the published Python engine (reticulate / PyCall), so the data is byte-identical to what the grader uses. Your strategy and agent code runs natively in your language.

Source