Skip to content

Backnrun is a Go trading framework for creating, testing and optimizing automated strategies, with support for technical indicators, backtesting and integration with exchanges.

License

Notifications You must be signed in to change notification settings

raykavin/backnrun-go

Repository files navigation

BackNRun

Go Reference Go Report Card License

BackNRun is a powerful, flexible trading bot framework written in Go. It provides a comprehensive set of tools for developing, backtesting, and optimizing trading strategies for cryptocurrency markets.

Preview of BackNRun 1

Preview of BackNRun 2

πŸ“Œ Roadmap & Features

  • Backtesting: Test strategies against historical data to evaluate performance
  • Strategy Development: Create custom trading strategies using a simple, extensible interface
  • Notifications - Telegram Notifications: Implemented notifications from Telegram channel
  • Parameter Optimization: Find optimal parameters for your strategies using grid search or random search
  • Performance Metrics: Analyze strategy performance with comprehensive metrics
  • Web dashboard for live tracking: Plot trading results with indicators and trades

πŸ“ Architecture

BackNRun is built with a modular architecture that separates concerns and allows for easy extension:

πŸ“ backnrun/
β”œβ”€β”€ πŸ“ cmd/                  # Command-line application
β”œβ”€β”€ πŸ“ examples/             # Examples of usage
β”œβ”€β”€ πŸ“ images/               # Preview images
β”œβ”€β”€ πŸ“ internal/             # Internal packages
β”œβ”€β”€ πŸ“ core/                 # Core interfaces and types
β”œβ”€β”€ πŸ“ exchange/             # Exchange implementations
β”œβ”€β”€ πŸ“ indicator/            # Technical indicators
β”œβ”€β”€ πŸ“ logger/               # Logging utilities
β”œβ”€β”€ πŸ“ metric/               # Performance metrics
β”œβ”€β”€ πŸ“ notification/         # Notification systems
β”œβ”€β”€ πŸ“ optimizer/            # Strategy parameter optimization
β”œβ”€β”€ πŸ“ order/                # Order management
β”œβ”€β”€ πŸ“ plot/                 # Visualization tools
β”œβ”€β”€ πŸ“ storage/              # Data storage
β”œβ”€β”€ πŸ“ scripts/              # Scripts utilities
β”œβ”€β”€ πŸ“ strategies/           # Presets strategies
β”œβ”€β”€ πŸ“ bot/                  # Bot implementations
└── πŸ“ strategy/             # Strategy implementations

πŸ“¦ Installation

Prerequisites

  • Go 1.23 or higher
  • Git

Building from Source

# Clone the repository
git clone https://github.com/raykavin/backnrun.git
cd backnrun

# Build the application
go build -o backnrun cmd/backnrun/main.go

⚑ Quick Start

Downloading Historical Data

# Download 30 days of BTC/USDT 15-minute candles
./backnrun download -p BTCUSDT -t 15m -d 30 -o btc-15m.csv

Running a Backtest

Create a Go file with your backtest configuration:

package main

import (
	"context"

	"github.com/raykavin/backnrun/bot"
	"github.com/raykavin/backnrun/strategies"
	"github.com/raykavin/backnrun/core"
	"github.com/raykavin/backnrun/exchange"
	
	"github.com/raykavin/backnrun/plot"
	"github.com/raykavin/backnrun/storage"
)

func main() {
	// Set up context and logging
	ctx := context.Background()
	log :=bot.DefaultLog
	log.SetLevel(core.DebugLevel)

	// Initialize trading strategy
	strategy := strategies.NewCrossEMA(9, 21, 10.0)

	// Configure trading pairs
	settings := &core.Settings{
		Pairs: []string{"BTCUSDT"},
	}

	// Initialize data feed
	dataFeed, err := exchange.NewCSVFeed(
		strategy.Timeframe(),
		exchange.PairFeed{
			Pair:      "BTCUSDT",
			File:      "btc-15m.csv",
			Timeframe: "15m",
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	// Initialize in-memory storage
	db, err := storage.FromMemory()
	if err != nil {
		log.Fatal(err)
	}

	// Initialize paper wallet
	wallet := exchange.NewPaperWallet(
		ctx,
		"USDT",
		log,
		exchange.WithPaperAsset("USDT", 1000),
		exchange.WithDataFeed(dataFeed),
	)

	// Initialize chart
	chart, err := plot.NewChart(
		log,
		plot.WithStrategyIndicators(strategy),
		plot.WithPaperWallet(wallet),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Set up the trading bot
	bot, err := bot.NewBot(
		ctx,
		settings,
		wallet,
		strategy,
		log,
		bot.WithBacktest(wallet),
		bot.WithStorage(db),
		bot.WithCandleSubscription(chart),
		bot.WithOrderSubscription(chart),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Run simulation
	if err := bot.Run(ctx); err != nil {
		log.Fatal(err)
	}

	// Display results
	bot.Summary()

	// Show interactive chart
	if err := chart.Start(); err != nil {
		log.Fatal(err)
	}
}

Run the backtest:

go run backtest.go

πŸ€– Available Strategies

BackNRun comes with several example strategies:

  • EMA Cross: Trading based on exponential moving average crossovers
  • MACD Divergence: Trading based on MACD indicator divergence
  • Triple EMA Cross: Trading using three exponential moving averages
  • Trend Master: Trend-following strategy with multiple indicators
  • Turtle Trading: Implementation of the famous Turtle Trading system
  • Larry Williams 91: Based on Larry Williams' trading methodology
  • Trailing Stop: Strategy with dynamic trailing stop-loss
  • OCO Sell: One-Cancels-the-Other order strategy

Creating Custom Strategies

To create a custom strategy, implement the core.Strategy interface:

type Strategy interface {
	// Timeframe is the time interval in which the strategy will be executed. eg: 1h, 1d, 1w
	Timeframe() string
	// WarmupPeriod is the necessary time to wait before executing the strategy, to load data for indicators.
	// This time is measured in the period specified in the `Timeframe` function.
	WarmupPeriod() int
	// Indicators will be executed for each new candle, in order to fill indicators before `OnCandle` function is called.
	Indicators(df *Dataframe) []ChartIndicator
	// OnCandle will be executed for each new candle, after indicators are filled, here you can do your trading logic.
	// OnCandle is executed after the candle close.
	OnCandle(df *Dataframe, broker Broker)
}

Example of a simple strategy:

type MyStrategy struct {
	// Strategy parameters
	fastPeriod int
	slowPeriod int
}

func NewMyStrategy() *MyStrategy {
	return &MyStrategy{
		fastPeriod: 9,
		slowPeriod: 21,
	}
}

func (s *MyStrategy) Timeframe() string {
	return "1h"
}

func (s *MyStrategy) WarmupPeriod() int {
	return 100
}

func (s *MyStrategy) Indicators(df *core.Dataframe) []core.ChartIndicator {
	// Calculate indicators
	df.Metadata["fast"] = indicator.EMA(df.Close, s.fastPeriod)
	df.Metadata["slow"] = indicator.EMA(df.Close, s.slowPeriod)

	// Return chart indicators for visualization
	return []core.ChartIndicator{
		{
			Overlay: true,
			GroupName: "Moving Averages",
			Time: df.Time,
			Metrics: []core.IndicatorMetric{
				{
					Values: df.Metadata["fast"],
					Name:   "Fast EMA",
					Color:  "red",
					Style:  core.StyleLine,
				},
				{
					Values: df.Metadata["slow"],
					Name:   "Slow EMA",
					Color:  "blue",
					Style:  core.StyleLine,
				},
			},
		},
	}
}

func (s *MyStrategy) OnCandle(ctx context.Context, df *core.Dataframe, broker core.Broker) {
	// Get current position
	assetPosition, quotePosition, err := broker.Position(df.Pair)
	if err != nil {
		return
	}

	// Get indicator values
	fast := df.Metadata["fast"].Last(0)
	slow := df.Metadata["slow"].Last(0)
	
	// Buy signal: fast EMA crosses above slow EMA
	if fast > slow && df.Metadata["fast"].Last(1) <= df.Metadata["slow"].Last(1) && quotePosition > 0 {
		// Calculate position size
		price := df.Close.Last(0)
		amount := quotePosition / price
		
		// Create market buy order
		broker.CreateOrderMarket(core.SideTypeBuy, df.Pair, amount)
	}
	
	// Sell signal: fast EMA crosses below slow EMA
	if fast < slow && df.Metadata["fast"].Last(1) >= df.Metadata["slow"].Last(1) && assetPosition > 0 {
		// Create market sell order
		broker.CreateOrderMarket(core.SideTypeSell, df.Pair, assetPosition)
	}
}

πŸ“Š Parameter Optimization

BackNRun includes a powerful parameter optimization package that helps you find the best parameters for your trading strategies. The optimizer supports multiple algorithms and performance metrics.

Optimization Algorithms

  • Grid Search: Exhaustively tests all combinations of parameter values within specified ranges
  • Random Search: Tests random combinations of parameter values, which can be more efficient for high-dimensional parameter spaces

Performance Metrics

The optimizer tracks various performance metrics:

  • profit: Total profit
  • win_rate: Percentage of winning trades
  • payoff: Payoff ratio (average win / average loss)
  • profit_factor: Profit factor (gross profit / gross loss)
  • sqn: System Quality Number
  • drawdown: Maximum drawdown
  • sharpe_ratio: Sharpe ratio
  • trade_count: Total number of trades

Example: Optimizing a Strategy

package main

import (
	"context"
	"fmt"
	"time"

	"github.com/raykavin/backnrun/bot"
	"github.com/raykavin/backnrun/strategies"
	"github.com/raykavin/backnrun/core"
	"github.com/raykavin/backnrun/exchange"
	
	"github.com/raykavin/backnrun/optimizer"
)

func main() {
	// Set up context and logging
	ctx := context.Background()
	log := bot.DefaultLog
	log.SetLevel(core.InfoLevel)

	// Initialize data feed for backtesting
	dataFeed, err := exchange.NewCSVFeed(
		"15m",
		exchange.PairFeed{
			Pair:      "BTCUSDT",
			File:      "btc-15m.csv",
			Timeframe: "15m",
		},
	)
	if err != nil {
		log.Fatal(err)
	}

	// Configure trading pairs
	settings := &core.Settings{
		Pairs: []string{"BTCUSDT"},
	}

	// Create strategy factory for EMA Cross strategy
	strategyFactory := func(params optimizer.ParameterSet) (core.Strategy, error) {
		// Extract parameters with validation
		emaLength, ok := params["emaLength"].(int)
		if !ok {
			return nil, fmt.Errorf("emaLength must be an integer")
		}

		smaLength, ok := params["smaLength"].(int)
		if !ok {
			return nil, fmt.Errorf("smaLength must be an integer")
		}

		minQuoteAmount, ok := params["minQuoteAmount"].(float64)
		if !ok {
			return nil, fmt.Errorf("minQuoteAmount must be a float")
		}

		// Create and return the strategy
		return strategies.NewCrossEMA(emaLength, smaLength, minQuoteAmount), nil
	}

	// Create evaluator
	evaluator := optimizer.NewBacktestStrategyEvaluator(
		strategyFactory,
		settings,
		dataFeed,
		log,
		1000.0, // Starting balance
		"USDT", // Quote currency
	)

	// Define parameters for optimization
	parameters := []optimizer.Parameter{
		{
			Name:        "emaLength",
			Description: "Length of the EMA indicator",
			Default:     9,
			Min:         3,
			Max:         50,
			Step:        1,
			Type:        optimizer.TypeInt,
		},
		{
			Name:        "smaLength",
			Description: "Length of the SMA indicator",
			Default:     21,
			Min:         5,
			Max:         100,
			Step:        5,
			Type:        optimizer.TypeInt,
		},
		{
			Name:        "minQuoteAmount",
			Description: "Minimum quote currency amount for trades",
			Default:     10.0,
			Min:         1.0,
			Max:         100.0,
			Step:        5.0,
			Type:        optimizer.TypeFloat,
		},
	}

	// Configure optimizer
	config := optimizer.NewConfig().
		WithParameters(parameters...).
		WithMaxIterations(50).
		WithParallelism(4).
		WithLogger(log).
		WithTargetMetric(optimizer.MetricProfit, true)

	// Create grid search optimizer
	gridSearch, err := optimizer.NewGridSearch(config)
	if err != nil {
		log.Fatal(err)
	}

	// Run optimization
	fmt.Println("Starting grid search optimization...")
	startTime := time.Now()

	results, err := gridSearch.Optimize(
		ctx,
		evaluator,
		optimizer.MetricProfit,
		true,
	)
	if err != nil {
		log.Fatal(err)
	}

	duration := time.Since(startTime)
	fmt.Printf("Optimization completed in %s\n", duration.Round(time.Second))

	// Print results
	optimizer.PrintResults(results, optimizer.MetricProfit, 5)

	// Save results to CSV
	outputFile := "ema_optimization_results.csv"
	if err := optimizer.SaveResultsToCSV(results, optimizer.MetricProfit, outputFile); err != nil {
		log.Errorf("Failed to save results: %v", err)
	} else {
		fmt.Printf("Results saved to %s\n", outputFile)
	}
}

πŸ›‘ Live Trading

To use BackNRun for live trading, you need to configure it with a real exchange:

package main

import (
	"context"

	"github.com/raykavin/backnrun/bot"
	"github.com/raykavin/backnrun/strategies"
	"github.com/raykavin/backnrun/core"
	"github.com/raykavin/backnrun/exchange/binance"
	
	"github.com/raykavin/backnrun/notification"
)

func main() {
	// Set up context and logging
	ctx := context.Background()
	log :=bot.DefaultLog
	log.SetLevel(core.InfoLevel)

	// Initialize trading strategy
	strategy := strategies.NewCrossEMA(9, 21, 10.0)

	// Configure trading pairs
	settings := &core.Settings{
		Pairs: []string{"BTCUSDT"},
	}

	// Initialize Binance exchange
	exchange, err := binance.NewExchange(ctx, log, binance.Config{
		Type:      binance.MarketTypeSpot,
		ApiKey:    "YOUR_API_KEY",
		ApiSecret: "YOUR_API_SECRET",
	})
	if err != nil {
		log.Fatal(err)
	}

	// Initialize Telegram notifications (optional)
	telegramNotifier, err := notification.NewTelegram(
		"YOUR_TELEGRAM_BOT_TOKEN",
		"YOUR_TELEGRAM_CHAT_ID",
		log,
	)
	if err != nil {
		log.Fatal(err)
	}

	// Set up the trading bot
	bot, err := bot.NewBot(
		ctx,
		settings,
		exchange,
		strategy,
		log,
		bot.WithTelegram(telegramNotifier),
	)
	if err != nil {
		log.Fatal(err)
	}

	// Run the bot
	if err := bot.Run(ctx); err != nil {
		log.Fatal(err)
	}
}

🀝 Contributing

Contributions to BackNRun are welcome! Here are some ways you can help improve the project:

  • Report bugs and suggest features by opening issues on GitHub
  • Submit pull requests with bug fixes or new features
  • Improve documentation to help other users and developers
  • Share your custom strategies with the community

πŸ“„ License

BackNRun is distributed under the GNU General Public License v3.0.
For complete license terms and conditions, see the LICENSE file in the repository.

Copyright Β© Raykavin Meireles


πŸ“¬ Contact

For support, collaboration, or questions about BackNRun:

Email: raykavin.meireles@gmail.com
GitHub: @raykavin
LinkedIn: @raykavin.dev
Instagram: @raykavin.dev

About

Backnrun is a Go trading framework for creating, testing and optimizing automated strategies, with support for technical indicators, backtesting and integration with exchanges.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published