For traders and strategy developers
Build your
trading bot
Build a trading strategy, test it on history and inspect every trade. Write the code yourself or with AI help. XTester Studio is a free desktop app. Run your strategy as a standalone robot — explore Launcher.
Download XTesterStart opens the live chart. Report shows trades and drawdown. Changing language resets the demo.
MARKET DATA
Data from 20 exchanges and 3 DEXs
Download historical data for spot and futures strategies, including linear and inverse contracts.
Available instruments and history depth vary by exchange.
01 / Your rules
Keep the logic in your hands
Define entries, exits, position size and risk limits. Start from a template or bring a file, folder or Git repository. Your strategy stays a project you can read and change.
C# has the fullest support. Nine more language adapters are available experimentally; Pine supports a subset. Browse source templates →
- 01Entry condition
Define when the strategy opens a trade.
- 02Position size
Set the size of each position.
- 03Exit & risk limits
Define exits and limits on risk.
02 / Test and compare
Look beyond the final balance
Review entries, exits, costs and drawdown. Change the parameters, compare versions, then test on a period you did not use while refining the strategy.
The interactive example above uses generated candles. Its trades and decision log let you trace the calculation; they are not historical returns. Run and inspect the example ↑
03 / AI alongside your code
Ask about the rule you are changing
Ask the assistant to explain an entry, find an error or suggest a change. Connect Claude Code, Codex, Claude Desktop or Cursor through MCP to work with XTester Studio from your own AI client.
Projects and downloaded history stay on your computer. Cloud AI processes the data you send; external agent access requires your approval.
MCP / Claude Code · Codex · Claude Desktop · Cursor
Connect AI agents / MCP
Connect your client to the installed desktop app. Generate a config here, or browse the tools and connection guide.
Set up an AI client
From the first test to your own bot
Build your own trading bot
Version: 1.0.0-preview.1 · Windows · macOS · Linux
Source templates, ready to inspect
Choose a language and download a starting file for XTester Studio. C# has the fullest support; other adapters are experimental, and Pine support covers a subset of the language.
- C#
- Python
- JavaScript
- TypeScript
- Java
- C++
- Rust
- Go
- R
- Pine Script
Choose a language and open the source
main.cs
Core runtimeDownloadA starter file for XTester Studio. Add your entry and exit rules in the bar callback. Source
public class UserStrategy : IStrategy
{
// Services available to the strategy: orders, indicators, parameters,
// market data, and logging.
private IApi _api = null!;
private IIndicators _indicators = null!;
private IStrategyParams _params = null!;
private IMarketData _marketData = null!;
private ILogger _logger = null!;
public void OnInit(StrategyContext context)
{
_api = context.Api;
_indicators = context.Indicators;
_params = context.Parameters;
_marketData = context.MarketData;
_logger = context.Logger;
_logger.Info("UserStrategy initialized.");
}
public void OnTick(decimal? price = null)
{
}
// Called for every closed bar in every subscription.
// symbolKey identifies the exchange, market, pair, and timeframe.
public void OnBar(ISymbolKey symbolKey, IKline kline)
{
}
// Called exactly once when a Tester, Paper, or Live run finishes.
public void OnFinish(StrategyFinishContext context)
{
}
}main.py
Experimental SDK bridgeDownloadA starter file for XTester Studio. Add your entry and exit rules in the bar callback. Source
# Add your decisions in on_bar. self.context.api places orders (buy, sell, place_order,
# close_position); self.context.indicators reads the engine's indicators (rsi, sma, ema,
# macd, call("ADX", period=14)); self.context.logger writes to the strategy log.
from xtester import Strategy, OrderSide, OrderType
class UserStrategy(Strategy):
# symbol_key: exchange, market, symbol, timeframe of the closed bar.
# bar: open_time, close_time, open, high, low, close, volume as Decimal.
def on_init(self, context):
context.logger.info("UserStrategy initialized.")
def on_tick(self, price):
pass
def on_bar(self, symbol_key, bar):
pass
def on_finish(self, context):
pass
def create_strategy():
return UserStrategy()main.mjs
Experimental SDK bridgeDownloadEducational Tester example: one market buy on a bullish candle, one close request on a later bearish candle. One symbol, initially flat; acceptance is not a fill. Not for live trading. Source
// EDUCATIONAL TESTER EXAMPLE — one symbol, initially flat; not for live trading.
// First bullish candle: attempt a 0.01 market buy. First later bearish candle:
// attempt to close the whole position. At most one entry and one exit request.
// Acceptance is not a fill; errors stop the example. No retries, restart recovery,
// position reconciliation or risk controls. Choose valid size/funds in Studio.
// GETTING STARTED
// 1. Open Data and download candles for an exchange, market, and symbol.
// 2. Press F5: the XTester engine runs this strategy over the project's candles and
// executes orders, fees and balances itself; this file only decides.
// 3. Add your decisions in onBar. this.context.api places orders (buy, sell, placeOrder,
// closePosition); this.context.indicators reads the engine's indicators (rsi, sma, ema,
// macd, call('ADX', { period: 14 })); this.context.logger writes to the strategy log.
// Ask the AI assistant to explain this file.
import { Strategy, OrderSide, OrderType } from 'xtester';
export class UserStrategy extends Strategy {
phase = 0;
// symbolKey: exchange, market, symbol, timeframe of the closed bar.
// bar: openTime, closeTime, open, high, low, close, volume as numbers (bar.text.* keeps the exact decimals).
onInit(context) {
context.logger.info('UserStrategy initialized.');
}
onTick(price) {
}
onBar(symbolKey, bar) {
if (this.phase === 0 && bar.close > bar.open) {
const result = this.context.api.buy('0.01');
this.phase = result.success ? 1 : 2;
if (!result.success) this.context.logger.info(result.errorMessage);
} else if (this.phase === 1 && bar.close < bar.open) {
const result = this.context.api.closePosition();
this.phase = 2;
if (!result.success) this.context.logger.info(result.errorMessage);
}
}
onFinish(context) {
}
}
export function createStrategy() {
return new UserStrategy();
}
main.ts
Experimental SDK bridgeDownloadA starter file for XTester Studio. Add your entry and exit rules in the bar callback. Source
// The XTester engine type-checks this strategy, runs it over the project's candles
// and executes orders, fees and balances itself; this file only decides.
import { Strategy, OrderSide, OrderType, type Bar, type Context, type SymbolKey } from 'xtester';
export class UserStrategy extends Strategy {
// symbolKey: exchange, market, symbol, timeframe of the closed bar.
// bar: openTime, closeTime, open, high, low, close, volume as numbers.
onInit(context: Context): void {
context.logger.info('UserStrategy initialized.');
}
onTick(price: number | undefined): void {
}
onBar(symbolKey: SymbolKey, bar: Bar): void {
}
onFinish(context: Context): void {
}
}
export function createStrategy(): UserStrategy {
return new UserStrategy();
}main.java
Experimental SDK bridgeDownloadEducational Tester example: one market buy on a bullish candle, one close request on a later bearish candle. One symbol, initially flat; acceptance is not a fill. Not for live trading. Source
// EDUCATIONAL TESTER EXAMPLE — one symbol, initially flat; not for live trading.
// First bullish candle: attempt a 0.01 market buy. First later bearish candle:
// attempt to close the whole position. At most one entry and one exit request.
// Acceptance is not a fill; errors stop the example. No retries, restart recovery,
// position reconciliation or risk controls. Choose valid size/funds in Studio.
// GETTING STARTED
// 1. Open Data and download candles for an exchange, market, and symbol.
// 2. Press F5: the XTester engine runs this strategy over the project's candles and
// executes orders, fees and balances itself; this file only decides. A JDK is needed:
// Studio offers the Java language pack when none is installed.
// 3. Add your decisions in onBar. context.api() places orders (buy, sell, placeOrder,
// closePosition); context.indicators() reads the engine's indicators (rsi, sma, ema,
// macd, call("ADX", "period", 14)); context.logger() writes to the strategy log.
// Ask the AI assistant to explain this file.
import xtester.Bar;
import xtester.Context;
import xtester.Strategy;
import xtester.SymbolKey;
import java.math.BigDecimal;
class UserStrategy extends Strategy {
private int phase = 0;
@Override
public void onInit(Context context) {
context.logger().info("UserStrategy initialized: " + context.parameters().symbolKey());
}
@Override
public void onTick(BigDecimal price) {
}
@Override
public void onBar(SymbolKey symbolKey, Bar bar) {
if (phase == 0 && bar.close().compareTo(bar.open()) > 0) {
var result = context.api().buy("0.01");
phase = result.success() ? 1 : 2;
if (!result.success()) context.logger().info(result.errorMessage());
} else if (phase == 1 && bar.close().compareTo(bar.open()) < 0) {
var result = context.api().closePosition();
phase = 2;
if (!result.success()) context.logger().info(result.errorMessage());
}
}
@Override
public void onFinish(Context context) {
}
}
main.cpp
Experimental SDK bridgeDownloadEducational Tester example: one market buy on a bullish candle, one close request on a later bearish candle. One symbol, initially flat; acceptance is not a fill. Not for live trading. Source
// EDUCATIONAL TESTER EXAMPLE — one symbol, initially flat; not for live trading.
// First bullish candle: attempt a 0.01 market buy. First later bearish candle:
// attempt to close the whole position. At most one entry and one exit request.
// Acceptance is not a fill; errors stop the example. No retries, restart recovery,
// position reconciliation or risk controls. Choose valid size/funds in Studio.
// GETTING STARTED
// 1. Open Data and download candles for an exchange, market, and symbol.
// 2. Press F5 inside a project: the XTester engine compiles every .cpp of this folder with the
// header-only xtester.hpp and runs it over the project's candles, executing orders, fees and
// balances itself; this file only decides. A C++ compiler is needed (Xcode CLT, clang/g++).
// 3. Add your decisions in onBar. ctx.api places orders (buy, sell, placeOrder, closePosition);
// ctx.indicators reads the engine's indicators (rsi, sma, ema, call("ADX", ...)); ctx.logger
// writes to the strategy log. XTESTER_STRATEGY defines main: never write main yourself.
// Ask the AI assistant to explain this file.
#include "xtester.hpp"
class UserStrategy : public xtester::Strategy {
int phase = 0;
void onInit(xtester::Context& ctx) override {
ctx.logger.info("UserStrategy initialized: " + ctx.parameters.symbolKey.str());
}
void onTick(xtester::Context& ctx, std::optional<double> price) override {
}
void onBar(xtester::Context& ctx, const xtester::SymbolKey& symbolKey, const xtester::Bar& bar) override {
if (phase == 0 && bar.close > bar.open) {
auto result = ctx.api.buy("0.01");
phase = result.success ? 1 : 2;
if (!result.success) ctx.logger.info(result.errorMessage);
} else if (phase == 1 && bar.close < bar.open) {
auto result = ctx.api.closePosition();
phase = 2;
if (!result.success) ctx.logger.info(result.errorMessage);
}
}
void onFinish(xtester::Context& ctx) override {
}
};
XTESTER_STRATEGY(UserStrategy)
main.rs
Experimental SDK bridgeDownloadEducational Tester example: one market buy on a bullish candle, one close request on a later bearish candle. One symbol, initially flat; acceptance is not a fill. Not for live trading. Source
// EDUCATIONAL TESTER EXAMPLE — one symbol, initially flat; not for live trading.
// First bullish candle: attempt a 0.01 market buy. First later bearish candle:
// attempt to close the whole position. At most one entry and one exit request.
// Acceptance is not a fill; errors stop the example. No retries, restart recovery,
// position reconciliation or risk controls. Choose valid size/funds in Studio.
// GETTING STARTED
// 1. Open Data and download candles for an exchange, market, and symbol.
// 2. Press F5 inside a project: the XTester engine builds this folder as a crate around the
// xtester SDK and runs it over the project's candles, executing orders, fees and balances
// itself; this file only decides. A Rust toolchain is needed: Studio offers the Rust language pack.
// 3. Add your decisions in on_bar. ctx.api places orders (buy, sell, place_order, close_position);
// ctx.indicators reads the engine's indicators (rsi, sma, ema, call("ADX", ...)); ctx.logger
// writes to the strategy log (println! breaks the protocol). The engine generates main.
// Ask the AI assistant to explain this file.
use xtester::*;
#[derive(Default)]
struct UserStrategy { phase: u8 }
impl Strategy for UserStrategy {
fn on_init(&mut self, ctx: &Context) {
ctx.logger.info(format!("UserStrategy initialized: {}", ctx.parameters.symbol_key));
}
fn on_tick(&mut self, ctx: &Context, price: Option<f64>) {
}
fn on_bar(&mut self, ctx: &Context, symbol_key: &SymbolKey, bar: &Bar) {
if self.phase == 0 && bar.close > bar.open {
let result = ctx.api.buy("0.01");
self.phase = if result.success { 1 } else { 2 };
if !result.success { ctx.logger.info(result.error_message); }
} else if self.phase == 1 && bar.close < bar.open {
let result = ctx.api.close_position(None, None);
self.phase = 2;
if !result.success { ctx.logger.info(result.error_message); }
}
}
fn on_finish(&mut self, ctx: &Context) {
}
}
pub fn create_strategy() -> Box<dyn Strategy> {
Box::new(UserStrategy::default())
}
main.go
Experimental SDK bridgeDownloadEducational Tester example: one market buy on a bullish candle, one close request on a later bearish candle. One symbol, initially flat; acceptance is not a fill. Not for live trading. Source
// EDUCATIONAL TESTER EXAMPLE — one symbol, initially flat; not for live trading.
// First bullish candle: attempt a 0.01 market buy. First later bearish candle:
// attempt to close the whole position. At most one entry and one exit request.
// Acceptance is not a fill; errors stop the example. No retries, restart recovery,
// position reconciliation or risk controls. Choose valid size/funds in Studio.
// GETTING STARTED
// 1. Open Data and download candles for an exchange, market, and symbol.
// 2. Press F5 inside a project: the XTester engine builds this folder (package main) with the
// xtester SDK and runs it over the project's candles, executing orders, fees and balances
// itself; this file only decides. A Go toolchain is needed: Studio offers the Go language pack.
// 3. Add your decisions in OnBar. ctx.Api places orders (Buy, Sell, PlaceOrder, ClosePosition);
// ctx.Indicators reads the engine's indicators (RSI, SMA, EMA, Call("ADX", ...)); ctx.Logger
// writes to the strategy log. Do not write func main: the engine generates it.
// Ask the AI assistant to explain this file.
package main
import (
"fmt"
"xtester"
)
type UserStrategy struct {
xtester.Base
phase int
}
func init() { xtester.Register(func() xtester.Strategy { return &UserStrategy{} }) }
func (s *UserStrategy) OnInit(ctx *xtester.Context) {
ctx.Logger.Info(fmt.Sprintf("UserStrategy initialized: %s", ctx.Parameters.SymbolKey))
}
func (s *UserStrategy) OnTick(ctx *xtester.Context, price *float64) {
}
func (s *UserStrategy) OnBar(ctx *xtester.Context, symbolKey xtester.SymbolKey, bar xtester.Bar) {
if s.phase == 0 && bar.Close > bar.Open {
result := ctx.Api.Buy("0.01")
if result.Success { s.phase = 1 } else { s.phase = 2; ctx.Logger.Info(result.ErrorMessage) }
} else if s.phase == 1 && bar.Close < bar.Open {
result := ctx.Api.ClosePosition(nil, "")
s.phase = 2
if !result.Success { ctx.Logger.Info(result.ErrorMessage) }
}
}
func (s *UserStrategy) OnFinish(ctx *xtester.Context) {
}
main.r
Experimental SDK bridgeDownloadEducational Tester example: one market buy on a bullish candle, one close request on a later bearish candle. One symbol, initially flat; acceptance is not a fill. Not for live trading. Source
# EDUCATIONAL TESTER EXAMPLE — one symbol, initially flat; not for live trading.
# First bullish candle: attempt a 0.01 market buy. First later bearish candle:
# attempt to close the whole position. At most one entry and one exit request.
# Acceptance is not a fill; errors stop the example. No retries, restart recovery,
# position reconciliation or risk controls. Choose valid size/funds in Studio.
# GETTING STARTED
# 1. Open Data and download candles for an exchange, market, and symbol.
# 2. Press F5 inside a project: the XTester engine runs this file with Rscript over the
# project's candles, executing orders, fees and balances itself; this file only decides.
# R must be installed (https://cloud.r-project.org); the R pack adds jsonlite and the language server.
# 3. Add your decisions in on_bar. context$api places orders (buy, sell, place_order,
# close_position); context$indicators reads the engine's indicators (rsi, sma, ema,
# call("ADX", list(period = 14))); context$logger writes to the strategy log.
# Ask the AI assistant to explain this file.
create_strategy <- function() {
state <- new.env()
state$phase <- 0L
list(
on_init = function(context) {
context$logger$info(paste("UserStrategy initialized:", xtester_symbol_text(context$parameters$symbol_key)))
},
on_tick = function(context, price) {
},
on_bar = function(context, symbol_key, bar) {
if (state$phase == 0L && bar$close > bar$open) {
result <- context$api$buy("0.01")
state$phase <- if (result$success) 1L else 2L
if (!result$success) context$logger$info(result$error_message)
} else if (state$phase == 1L && bar$close < bar$open) {
result <- context$api$close_position()
state$phase <- 2L
if (!result$success) context$logger$info(result$error_message)
}
},
on_finish = function(context) {
}
)
}
main.pine
Pine subset interpreterDownloadOriginal EMA crossover strategy for the Pine subset interpreter. Source
//@version=6
// XTester's own Pine interpreter runs this script once per closed bar and
// the engine executes strategy.entry / strategy.exit / strategy.close orders,
// fees and balances. Plots and drawings are accepted and ignored.
strategy("UserStrategy", overlay = true, default_qty_type = strategy.percent_of_equity, default_qty_value = 10)
fast = ta.ema(close, input.int(9, "Fast EMA"))
slow = ta.ema(close, input.int(21, "Slow EMA"))
if ta.crossover(fast, slow)
strategy.entry("Long", strategy.long)
if ta.crossunder(fast, slow)
strategy.close("Long")
plot(fast, "Fast", color.new(color.blue, 0))
plot(slow, "Slow", color.new(color.orange, 0))XTester in action
Charts, strategy testing and an AI assistant in half a minute.
The embedded YouTube player connects when this page loads. Press play to watch over the page.
Music and license
“Signal to Noise” — Scott Buckley · CC BY 4.0.
Recording excerpt edited with fades, loudness normalization and mixing.
Your trading bot
From strategy to your own bot
Run your finished strategy in Launcher in Paper or Live mode. XTester Studio will assemble the bot package with the required components.
Execution conditions and CopyTrader
Your own bot
From strategy to your own bot
Run the finished strategy in Launcher using Paper or Live mode. XTester Studio can also build a robot package with the required runtime components.
Give the robot your chosen name and logo to distribute or sell it under your own brand (white-label). Build your own robot product → On your own server, you can run the robot in Paper mode or on HyperLiquid Testnet; Mainnet trading is not available through this remote setup. Server setup →
For subscribers who want to copy trades, CopyTrader is a separate service. Visit its site for connection terms.
More about CopyTrader ↗Explore Launcher →Paper and Live
A virtual account or your exchange account with API keys. Before Live, complete the checks in the app.
On your own server
Remote execution: Paper or Hyperliquid Testnet. Mainnet is not available in this setup.
Under your own brand
Your name and logo — to distribute or sell your bot (white-label).
Questions & answers
Documentation →Do I need to know how to code?
You can start by describing an idea. The specification wizard helps define the rules, and the AI assistant helps write and fix the code. In XTester Studio, AI features use your provider key or a local model. If you prefer to code yourself, the editor includes templates and hints.
What language are strategies written in?
C#, Python, JavaScript, TypeScript, Pine Script, Java, C++, Rust, Go and R. C# has the fullest support, including AI Lab. Other languages use experimental adapters, with differences in available libraries and calculation results.
What works locally and what needs the network?
The editor, projects, downloaded history, backtests and reports work on your computer without an account or exchange keys. You need the internet to download data, updates and language packs or use GitHub. For AI, choose a cloud provider or a local model.
How much does it cost?
XTester Studio is free, with no feature limits. External AI requests are billed by your chosen provider. You can also connect a local model.
Other questions
Can I bring over an existing strategy?
Yes. Give the conversion wizard a file, folder, archive, text, link or GitHub repository. It creates a new project and explains changes and unsupported features. Conversion requires an AI model. You can then compare entry and exit times and directions with a reference trade list; financial results need a separate review.
How compatible is Pine Script with TradingView?
XTester supports a subset of Pine Script v5/v6 and uses its own engine to calculate trades. Common indicator functions and trading commands are supported. Plots, drawings and alerts are ignored; library imports and some other features are unsupported. Differences in order execution, fees and slippage can produce different results from TradingView.
What else do I need to install for my language?
C#, JavaScript, TypeScript and Pine Script are ready to use with XTester Studio. The app offers language packs for Python, Java, Go and Rust; Python can also use your own interpreter. C++ needs a system compiler, and R needs an R installation. XTester Studio offers downloads for the additional components.
How is a backtest different from a robustness check?
A backtest shows the result over a chosen period. A robustness check reruns it with different fees and slippage and over individual market phases, so you can see how much the result depends on those conditions. The phases use the same history; this does not replace a test on new data.
Can I connect an external AI agent?
Yes. MCP gives a compatible client access to the project, data and test results. You approve the connection in XTester Studio and can restrict it to read-only access. One session at a time can make changes. More about MCP
Which exchanges are supported?
You can download history from the 20 centralized exchanges and 3 DEXs listed above, including Binance, Bybit, OKX and HyperLiquid. Spot and futures data are available; instruments and history depth depend on the exchange.
Can I trade live from XTester?
Yes, through XTester Launcher, which comes with XTester Studio. Open a finished strategy in Launcher and choose Paper for a virtual account or Live for your exchange account using API keys. Launcher runs checks before a Live start. Its window shows positions, orders and trades, with notifications including Telegram. You remain responsible for trading decisions.
Where is my data stored?
Projects stay on your disk. Telemetry requires your consent. If you connect a cloud AI provider, data sent to it is handled under its rules. You can use a local model instead.
What are the requirements?
You need 64-bit Windows, macOS or Linux. The download page lists system requirements and files for each platform. Some strategy languages need the additional tools described above.
Latest articles
Loading articles…