Quick Reference
API cheat sheet for writing wisp strategies with the event-driven Start/run/Emit pattern.
The Wisp Context
Every strategy has access to the full Wisp SDK:
type MyStrategy struct {
w wisp.Wisp // Your gateway to everything
}
// In Start() method:
func (s *MyStrategy) Start(ctx context.Context) error {
go s.run(ctx)
return nil
}
// In run() method, access services via s.w:
// - s.w.Asset("BTC") // Get asset
// - s.w.Pair(btc, usdt) // Create pair
// - s.w.Indicators() // Technical analysis
// - s.w.Spot() // Spot trading & data
// - s.w.Perp() // Perpetuals trading
// - s.w.Predict() // Prediction markets
// - s.w.Activity() // Positions, balances
// - s.w.Log() // Structured logging
// - s.w.Emit(signal) // Send signal to executor
Lifecycle Methods
Every strategy must implement:
func (s *MyStrategy) Start(ctx context.Context) error // Launch run() goroutine
func (s *MyStrategy) Stop(ctx context.Context) error // Clean shutdown
func (s *MyStrategy) GetName() strategy.StrategyName // Name identifier
func (s *MyStrategy) Signals() <-chan strategy.Signal // Signal stream
Assets
Get references to assets you want to trade:
btc := s.k.Asset("BTC")
eth := s.k.Asset("ETH")
sol := s.k.Asset("SOL")
Assets are just references. wisp knows which exchange to use based on your config.
Indicators
All indicators follow the same pattern: pass the asset and parameters.
// RSI - Relative Strength Index
rsi := s.k.Indicators().RSI(btc, 14)
// SMA - Simple Moving Average
sma := s.k.Indicators().SMA(btc, 20)
// EMA - Exponential Moving Average
ema := s.k.Indicators().EMA(btc, 50)
// MACD - Moving Average Convergence Divergence
macd := s.k.Indicators().MACD(btc, 12, 26, 9)
s.k.Log().Debug("MACD", btc.Symbol(), "MACD: %s, Signal: %s, Histogram: %s",
macd.MACD, macd.Signal, macd.Histogram)
// Bollinger Bands
bb := s.k.Indicators().BollingerBands(btc, 20, 2.0)
s.k.Log().Debug("BB", btc.Symbol(), "Upper: %s, Middle: %s, Lower: %s",
bb.Upper, bb.Middle, bb.Lower)
// Stochastic Oscillator
stoch := s.k.Indicators().Stochastic(btc, 14, 3)
s.k.Log().Debug("Stochastic", btc.Symbol(), "K: %s, D: %s", stoch.K, stoch.D)
// ATR - Average True Range
atr := s.k.Indicators().ATR(btc, 14)
wisp automatically:
- Fetches the required price data
- Calculates the indicator
- Returns the latest value
Indicator Options
Specify exchange or interval when needed:
import "github.com/wisp-trading/sdk/pkg/wisp/indicators"
import "github.com/wisp-trading/sdk/pkg/types/connector"
// Use specific exchange
rsi := s.k.Indicators().RSI(btc, 14, indicators.IndicatorOptions{
Exchange: connector.Binance,
})
// Use different timeframe
sma := s.k.Indicators().SMA(btc, 200, indicators.IndicatorOptions{
Interval: "4h",
})
// Both
ema := s.k.Indicators().EMA(btc, 50, indicators.IndicatorOptions{
Exchange: connector.Bybit,
Interval: "1h",
})
Market Data
Access real-time market data:
// Current price
price := s.k.Market().Price(btc)
// Price from specific exchange
price := s.k.Market().Price(btc, market.MarketOptions{
Exchange: connector.Binance,
})
// Prices from all exchanges
prices := s.k.Market().Prices(btc)
for exchange, price := range prices {
s.k.Log().Info("%s: %s", exchange, price)
}
// Order book
book := s.k.Market().OrderBook(btc)
topBid := book.Bids[0] // Best bid
topAsk := book.Asks[0] // Best ask
// Funding rate (perpetuals)
funding := s.k.Market().FundingRate(btc)
// Historical klines
klines := s.k.Market().Klines(btc, "1h", 100) // Last 100 1h candles
Domain APIs: Spot, Perp, Predict
Access trading markets through domain-specific APIs:
Spot Markets (Buying & Selling)
// Watch a pair for price updates
btc := s.w.Asset("BTC")
usdt := s.w.Asset("USDT")
pair := s.w.Pair(btc, usdt)
s.w.Spot().WatchPair(connector.Binance, pair)
// Get current price
price := s.w.Spot().Price(pair)
price := s.w.Spot().Price(pair, connector.Binance) // Specific exchange
// Create and emit buy signal
signal := s.w.Spot().Signal(s.name).
BuyMarket(pair, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.w.Emit(signal)
// Sell signal
signal := s.w.Spot().Signal(s.name).
SellMarket(pair, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.w.Emit(signal)
// Limit orders
signal := s.w.Spot().Signal(s.name).
BuyLimit(pair, connector.Binance, decimal.NewFromFloat(0.1), decimal.NewFromInt(45000)).
Build()
s.w.Emit(signal)
Perpetual Futures Markets (Leveraged Trading)
// Watch futures pair
pair := s.w.Pair(btc, usdt)
s.w.Perp().WatchPair(connector.Bybit, pair)
// Get price
price := s.w.Perp().Price(pair)
// Leveraged long with 5x
signal := s.w.Perp().Signal(s.name).
BuyMarket(pair, connector.Bybit,
decimal.NewFromFloat(0.1), // quantity
decimal.NewFromInt(5)). // 5x leverage
Build()
s.w.Emit(signal)
// Leveraged short with 3x
signal := s.w.Perp().Signal(s.name).
SellMarket(pair, connector.Bybit,
decimal.NewFromFloat(0.1), // quantity
decimal.NewFromInt(3)). // 3x leverage
Build()
s.w.Emit(signal)
// Get funding rate (positive = longs pay shorts)
fundingRate := s.w.Perp().FundingRate(pair)
Prediction Markets
// Watch a prediction market
s.w.Predict().WatchMarket(connector.Polymarket,
"BTC above $50k by Jan 2026")
// Buy shares in an outcome
signal := s.w.Predict().Signal(s.name).
Buy(market, outcome,
connector.Polymarket,
shares, // How many shares
maxPrice, // Max price per share
expiry). // Expiration time
Build()
s.w.Emit(signal)
Emitting Signals
The event-driven pattern: analyze in your run loop, emit signals asynchronously:
// In your run() loop:
func (s *MyStrategy) run(ctx context.Context) {
ticker := time.NewTicker(1 * time.Hour)
defer ticker.Stop()
for {
select {
case <-ticker.C:
// Analyze market
rsi := s.w.Indicators().RSI(pair, 14)
// When signal condition met, emit asynchronously
if rsi.LessThan(decimal.NewFromInt(30)) {
signal := s.w.Spot().Signal(s.name).
BuyMarket(pair, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.w.Emit(signal) // Push to executor
s.w.Log().Opportunity(string(s.name), "BTC", "RSI oversold")
}
case <-ctx.Done():
return
}
}
}
Working with Decimals
wisp uses decimal.Decimal for all financial calculations:
import "github.com/shopspring/decimal"
// Create decimals
price := decimal.NewFromFloat(50000.50)
quantity := decimal.NewFromInt(2)
pct := decimal.NewFromString("0.025") // 2.5%
// Math operations
total := price.Mul(quantity)
fee := total.Mul(pct)
net := total.Sub(fee)
// Comparisons
if price.GreaterThan(decimal.NewFromInt(50000)) {
// Price above 50k
}
if rsi.LessThan(decimal.NewFromInt(30)) {
// RSI oversold
}
// String conversion
s.k.Log().Info("Price: %s", price.String()) // "50000.5"
s.k.Log().Info("Price fixed: %s", price.StringFixed(2)) // "50000.50"
Always use decimal.Decimal to avoid floating-point precision errors.
Logging
Use structured logging to track your strategy:
// Info level
s.k.Log().Info("Strategy initialized")
// Debug level
s.k.Log().Debug("Strategy", btc.Symbol(), "RSI: %s", rsi)
// Market conditions
s.k.Log().MarketCondition("Price: %s, RSI: %s", price, rsi)
// Opportunities
s.k.Log().Opportunity("Strategy", btc.Symbol(),
"Buy signal - RSI oversold at %s", rsi)
// Failures
s.k.Log().Failed("Strategy", btc.Symbol(),
"Failed to calculate RSI: %v", err)
Error Handling
Always handle errors properly:
func (s *Strategy) GetSignals() ([]*strategy.Signal, error) {
btc := s.k.Asset("BTC")
// Check if indicators have valid data
rsi := s.k.Indicators().RSI(btc, 14)
if rsi.IsZero() {
// No data yet, skip this cycle
return nil, nil
}
// Your logic here...
return signals, nil
}
Complete Example
A simple RSI strategy with the Start/run/Emit pattern:
package main
import (
"context"
"time"
"github.com/wisp-trading/sdk/pkg/types/connector"
"github.com/wisp-trading/sdk/pkg/types/wisp"
"github.com/wisp-trading/sdk/pkg/types/strategy"
"github.com/shopspring/decimal"
)
type RSIStrategy struct {
w wisp.Wisp
name strategy.StrategyName
signalChan chan strategy.Signal
stopChan chan struct{}
}
func NewRSI(w wisp.Wisp) *RSIStrategy {
return &RSIStrategy{
w: w,
name: strategy.Momentum,
signalChan: make(chan strategy.Signal, 10),
stopChan: make(chan struct{}),
}
}
// Start launches the run goroutine (non-blocking)
func (s *RSIStrategy) Start(ctx context.Context) error {
go s.run(ctx)
return nil
}
// run owns the execution loop
func (s *RSIStrategy) run(ctx context.Context) {
ticker := time.NewTicker(1 * time.Hour)
defer ticker.Stop()
btc := s.w.Asset("BTC")
usdt := s.w.Asset("USDT")
pair := s.w.Pair(btc, usdt)
// Watch the pair
s.w.Spot().WatchPair(connector.Binance, pair)
for {
select {
case <-s.stopChan:
return
case <-ctx.Done():
return
case <-ticker.C:
// Analyze market
rsi := s.w.Indicators().RSI(pair, 14)
price := s.w.Spot().Price(pair)
// Buy when oversold
if rsi.LessThan(decimal.NewFromInt(30)) {
signal := s.w.Spot().Signal(s.name).
BuyMarket(pair, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.w.Emit(signal)
s.w.Log().Opportunity(string(s.name), "BTC",
"RSI oversold at %.2f, Price=%.2f", rsi, price)
}
// Sell when overbought
if rsi.GreaterThan(decimal.NewFromInt(70)) {
signal := s.w.Spot().Signal(s.name).
SellMarket(pair, connector.Binance, decimal.NewFromFloat(0.1)).
Build()
s.w.Emit(signal)
s.w.Log().Opportunity(string(s.name), "BTC",
"RSI overbought at %.2f, Price=%.2f", rsi, price)
}
}
}
}
// Stop cleanly shuts down the strategy
func (s *RSIStrategy) Stop(ctx context.Context) error {
close(s.stopChan)
return nil
}
// Required interface methods
func (s *RSIStrategy) GetName() strategy.StrategyName {
return s.name
}
func (s *RSIStrategy) Signals() <-chan strategy.Signal {
return s.signalChan
}
func (s *RSIStrategy) LatestStatus() strategy.StrategyStatus {
return strategy.StrategyStatus{}
}
func (s *RSIStrategy) StatusLog() []strategy.StrategyStatus {
return []strategy.StrategyStatus{}
}
Best Practices
Start/run/Emit pattern:
- Non-blocking
Start()that launches a goroutine run()owns the execution loop with tickers/channelsEmit()pushes signals asynchronously (don't return from methods)Stop()cleanly shuts down the goroutine
Key methods:
- Use
s.w.Spot()for spot trading - Use
s.w.Perp()for perpetuals - Use
s.w.Indicators()for technical analysis - Use
s.w.Emit(signal)to send signals (not return) - Use
s.w.Log()for structured logging
Next Steps
- Writing Strategies - Deep dive into strategy patterns
- Examples - Common strategy implementations
- Indicators Reference - Full indicator documentation