Writing Strategies
Learn how to build sophisticated trading strategies with Kronos.
Strategy Structure
Every strategy implements the strategy.Strategy interface:
type Strategy interface {
GetSignals() ([]*Signal, error)
GetName() StrategyName
GetDescription() string
GetRiskLevel() RiskLevel
GetStrategyType() StrategyType
}
The most important method is GetSignals() - this is where your trading logic lives.
GetSignals()
Kronos calls GetSignals() on each interval (configured in config.yaml). Your job is to:
- Analyze market conditions
- Return trade signals if conditions are met
- Return
nil, nilif no action needed
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
// 1. Get your assets
btc := s.k.Asset("BTC")
// 2. Analyze market
rsi := s.k.Indicators().RSI(btc, 14)
price := s.k.Market().Price(btc)
// 3. Decide
if rsi.LessThan(decimal.NewFromInt(30)) {
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
return []*strategy.Signal{signal}, nil
}
// 4. No action
return nil, nil
}
Multiple Assets
Trade multiple assets in one strategy:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
eth := s.k.Asset("ETH")
sol := s.k.Asset("SOL")
var signals []*strategy.Signal
// Check each asset
for _, asset := range []types.Asset{btc, eth, sol} {
rsi := s.k.Indicators().RSI(asset, 14)
if rsi.LessThan(decimal.NewFromInt(30)) {
signal := s.k.Signal(s.GetName()).
Buy(asset, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
signals = append(signals, signal)
}
}
return signals, nil
}
Kronos automatically manages data for all assets in parallel.
Multiple Timeframes
Use different timeframes for trend and signals:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
// Long-term trend (4-hour)
sma200 := s.k.Indicators().SMA(btc, 200, indicators.IndicatorOptions{
Interval: "4h",
})
// Short-term signal (1-hour)
rsi := s.k.Indicators().RSI(btc, 14, indicators.IndicatorOptions{
Interval: "1h",
})
price := s.k.Market().Price(btc)
// Only buy if in uptrend AND oversold
if price.GreaterThan(sma200) && rsi.LessThan(decimal.NewFromInt(30)) {
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.k.Log().Opportunity(string(s.GetName()), "BTC", "Oversold in uptrend")
return []*strategy.Signal{signal}, nil
}
return nil, nil
}
Combining Indicators
Use multiple indicators for confirmation:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
// Get multiple indicators
rsi := s.k.Indicators().RSI(btc, 14)
stoch := s.k.Indicators().Stochastic(btc, 14, 3)
bb := s.k.Indicators().BollingerBands(btc, 20, 2.0)
price := s.k.Market().Price(btc)
// Require all three to confirm oversold
oversold := rsi.LessThan(decimal.NewFromInt(30)) &&
stoch.K.LessThan(decimal.NewFromInt(20)) &&
price.LessThan(bb.Lower)
if oversold {
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.15)). // Larger size with confirmation
Build()
s.k.Log().Opportunity(string(s.GetName()), "BTC", "Triple confirmation: RSI, Stoch, BB")
return []*strategy.Signal{signal}, nil
}
return nil, nil
}
Risk Management
Stop Loss & Take Profit
Use ATR for dynamic stops:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
rsi := s.k.Indicators().RSI(btc, 14)
price := s.k.Market().Price(btc)
atr := s.k.Indicators().ATR(btc, 14)
if rsi.LessThan(decimal.NewFromInt(30)) {
// Calculate stops based on ATR
// Stop loss at 2× ATR below entry
stopLoss := price.Sub(atr.Mul(decimal.NewFromInt(2)))
// Take profit at 3× ATR above entry
takeProfit := price.Add(atr.Mul(decimal.NewFromInt(3)))
// Log the stop levels for reference
s.k.Log().Info(string(s.GetName()), \"BTC\",
\"Entry: %s, Stop: %s, Target: %s (R:R 1:1.5)\",
price, stopLoss, takeProfit)
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
return []*strategy.Signal{signal}, nil
}
return nil, nil
}
Position Sizing
Size positions based on volatility:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
atr := s.k.Indicators().ATR(btc, 14)
price := s.k.Market().Price(btc)
// Risk 2% of account on this trade
accountBalance := decimal.NewFromInt(10000)
riskAmount := accountBalance.Mul(decimal.NewFromFloat(0.02)) // $200 risk
// Stop loss at 2× ATR
stopDistance := atr.Mul(decimal.NewFromInt(2))
// Position size = risk / stop distance
quantity := riskAmount.Div(stopDistance)
// Rest of logic...
}
Volatility Filter
Skip trading in extreme volatility:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
atr := s.k.Indicators().ATR(btc, 14)
price := s.k.Market().Price(btc)
// ATR as percentage of price
atrPercent := atr.Div(price).Mul(decimal.NewFromInt(100))
// Skip if volatility too high
if atrPercent.GreaterThan(decimal.NewFromInt(5)) {
s.k.Log().MarketCondition("Volatility too high: %s%%", atrPercent)
return nil, nil
}
// Normal trading logic...
}
Cross-Exchange Strategies
Arbitrage
Find price differences across exchanges:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
// Get prices from all exchanges
prices := s.k.Market().Prices(btc)
binancePrice := prices[connector.Binance]
bybitPrice := prices[connector.Bybit]
// Calculate spread
spread := binancePrice.Sub(bybitPrice).Div(bybitPrice).Mul(decimal.NewFromInt(100))
// If spread > 0.5%, arbitrage opportunity
if spread.GreaterThan(decimal.NewFromFloat(0.5)) {
s.k.Log().Opportunity(string(s.GetName()), \"BTC\", \"Spread: %.2f%%\", spread)
// Note: You need to specify quantity for each trade
qty := decimal.NewFromFloat(0.1)
return []*strategy.Signal{
s.k.Signal(s.GetName()).
Buy(btc, connector.Bybit, qty).
Sell(btc, connector.Binance, qty).
Build(),
}, nil
}
return nil, nil
}
Best Price Execution
Always trade on the exchange with best price:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
rsi := s.k.Indicators().RSI(btc, 14)
if rsi.LessThan(decimal.NewFromInt(30)) {
// Find exchange with lowest price
prices := s.k.Market().Prices(btc)
var bestExchange connector.ExchangeName
var bestPrice decimal.Decimal
for exchange, price := range prices {
if bestPrice.IsZero() || price.LessThan(bestPrice) {
bestPrice = price
bestExchange = exchange
}
}
s.k.Log().Info(string(s.GetName()), \"BTC\", \"Best price on %s: %s\", bestExchange, bestPrice)
signal := s.k.Signal(s.GetName()).
Buy(btc, bestExchange, decimal.NewFromFloat(0.1)).
Build()
return []*strategy.Signal{signal}, nil
}
return nil, nil
}
Advanced Patterns
Trend Following
Follow the trend with moving average crossovers:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
sma50 := s.k.Indicators().SMA(btc, 50)
sma200 := s.k.Indicators().SMA(btc, 200)
price := s.k.Market().Price(btc)
// Golden cross: 50 SMA crosses above 200 SMA
if sma50.GreaterThan(sma200) && price.GreaterThan(sma50) {
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.2)).
Build()
s.k.Log().Opportunity(string(s.GetName()), "BTC", "Golden cross + price above 50 SMA")
return []*strategy.Signal{signal}, nil
}
// Death cross: 50 SMA crosses below 200 SMA
if sma50.LessThan(sma200) {
signal := s.k.Signal(s.GetName()).
Sell(btc, connector.Binance, decimal.NewFromFloat(0.2)).
Build()
s.k.Log().Opportunity(string(s.GetName()), "BTC", "Death cross")
return []*strategy.Signal{signal}, nil
}
return nil, nil
}
Mean Reversion
Trade bounces from Bollinger Bands:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
bb := s.k.Indicators().BollingerBands(btc, 20, 2.0)
price := s.k.Market().Price(btc)
rsi := s.k.Indicators().RSI(btc, 14)
// Buy when price touches lower band AND RSI confirms
if price.LessThan(bb.Lower) && rsi.LessThan(decimal.NewFromInt(30)) {
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.k.Log().Opportunity(string(s.GetName()), "BTC",
"Mean reversion from lower BB, target middle: %s", bb.Middle)
return []*strategy.Signal{signal}, nil
}
return nil, nil
}
Breakout Trading
Trade breakouts with volume confirmation:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
bb := s.k.Indicators().BollingerBands(btc, 20, 2.0)
price := s.k.Market().Price(btc)
// Band width (volatility)
bandWidth := bb.Upper.Sub(bb.Lower).Div(bb.Middle).Mul(decimal.NewFromInt(100))
// Squeeze: bands narrow (breakout coming)
if bandWidth.LessThan(decimal.NewFromInt(10)) {
s.k.Log().MarketCondition("Bollinger squeeze detected")
// Buy on upward breakout
if price.GreaterThan(bb.Upper) {
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.15)).
Build()
s.k.Log().Opportunity(string(s.GetName()), "BTC", "Breakout from squeeze")
return []*strategy.Signal{signal}, nil
}
}
return nil, nil
}
State Management
Keep track of positions and state:
type TrendStrategy struct {
k *sdk.Kronos
inPosition bool
entryPrice decimal.Decimal
trailingStop decimal.Decimal
}
func (s *TrendStrategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
price := s.k.Market().Price(btc)
atr := s.k.Indicators().ATR(btc, 14)
// Entry logic
if !s.inPosition {
rsi := s.k.Indicators().RSI(btc, 14)
if rsi.LessThan(decimal.NewFromInt(30)) {
s.inPosition = true
s.entryPrice = price
s.trailingStop = price.Sub(atr.Mul(decimal.NewFromInt(2)))
signal := s.k.Signal(s.GetName()).
Buy(btc, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
return []*strategy.Signal{signal}, nil
}
}
// Exit logic (trailing stop)
if s.inPosition {
// Update trailing stop as price rises
newStop := price.Sub(atr.Mul(decimal.NewFromInt(2)))
if newStop.GreaterThan(s.trailingStop) {
s.trailingStop = newStop
}
// Exit if stop hit
if price.LessThan(s.trailingStop) {
s.inPosition = false
signal := s.k.Signal(s.GetName()).
Sell(btc, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.k.Log().Info(string(s.GetName()), "BTC", "Trailing stop hit")
return []*strategy.Signal{signal}, nil
}
}
return nil, nil
}
Best Practices
✅ Do
- Test thoroughly - Backtest extensively before live trading
- Use proper decimals - Always use
decimal.Decimalfor money - Handle errors - Check for data availability
- Log decisions - Use
s.k.Log()to track behavior - Start small - Begin with small position sizes
- Use stop losses - Protect against adverse moves
- Combine indicators - Use multiple confirmations
❌ Don't
- Don't use floats - Never use
float64for financial calculations - Don't overtrade - Avoid excessive signals
- Don't overfit - Don't optimize for past data
- Don't ignore risk - Always manage position sizes
- Don't skip backtesting - Always test before going live
Next Steps
- Examples - See complete strategy implementations
- Configuration - Configure exchanges and parameters
- Indicators Reference - Full indicator documentation