Skip to content

Repository files navigation

SolTrade Banner

License GitHub issues GitHub forks GitHub Release

Automated trading for Solana.

A hard fork of noahtheprogrammer/soltrade.

Warning

SolTrade trades real money on Solana mainnet. Start with small amounts you can afford to lose, test with a new wallet first, and never risk funds you can't spare. Not financial advice — you are responsible for your own trades.

Links

Quick Start

  1. Install uv:

    Windows

    powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

    Linux / macOS

    curl -LsSf https://astral.sh/uv/install.sh | sh
  2. Clone the repository and create the configuration file:

    git clone https://github.com/etcherfx/sol-trade.git
    cd sol-trade
    cp config.json.sample config.json
  3. Configure credentials and tokens:

    • Copy .env.sample to .env and set SOLTRADE_PRIVATE_KEY (your Solana wallet).
    • In config.json, set secondary_mints / secondary_mint_symbols — the token(s) you want to trade.
  4. Start the bot:

    uv run main.py

    Paper trade first — swaps are simulated and the wallet is never touched:

    uv run main.py --dry-run

Configuration

SolTrade reads its configuration from config.json and its credentials from the .env file, both in the project root. Copy config.json.sample to config.json and .env.sample to .env before the first run. Environment variables take precedence over config.json.

Config is hot-reloaded: edits to config.json are picked up automatically on the next trading cycle (≤ 1 minute) — strategy, feature toggles, slippage, whale wallets, polling intervals, and the traded tokens all apply live. Structural settings (the primary token you pay with, RPC, exchange) and .env credentials still require a restart. Removing a token that currently has an open position is refused until that position is closed.

Credentials

Secrets live in the git-ignored .env file:

Variable Purpose
SOLTRADE_PRIVATE_KEY Solana wallet private key (base58)
SOLTRADE_JUPITER_API_KEY Jupiter API key — optional, sent only if set

Core settings

Setting What it does Default
rpc_https Solana RPC endpoint for balances and token data https://api.mainnet-beta.solana.com
jup_api Jupiter Swap API endpoint https://api.jup.ag/swap/v2
primary_mint / primary_mint_symbol The token you pay with (usually a stablecoin) EPjF..v / USDC
secondary_mints / secondary_mint_symbols The token(s) you want to trade [So11..2] / [SOL]
sol_mint Mint address of native SOL So11111111111111111111111111111111111111112
price_update_seconds How often token prices refresh 60
max_slippage Maximum accepted slippage in BPS (100 BPS = 1%) 50
strategy The strategy to trade with default
secondary_weights Portfolio weight per token (parallel to secondary_mints); empty = equal split []
token_strategies Per-token strategy overrides, e.g. {"SOL": "default", "JUP": "momentum"} {}
token_exchanges Per-token candle-data exchange overrides, e.g. {"POPCAT": "mexc"}; missing = data_exchange {}
data_exchange Exchange used for candlestick data (via ccxt) okx
candles_path Local SQLite store for candlestick history data/candles.db

Advanced settings

Setting What it does Default
whale_tracking_enabled Poll configured whale wallets and produce signals true
whale_wallets Wallet addresses to watch, per token symbol {}
whale_poll_interval_minutes How often whale balances are polled 5
confluence_enabled Route every trade through the confluence filter true
market_regime_enabled Scale position sizes by market regime false
sentiment_enabled Pause trading when sentiment crashes false
sentiment_pause_hours How long a sentiment block lasts 4
sentiment_threshold Per-token block threshold (-1 to +1) -0.5
sentiment_crash_threshold Market-wide crash threshold (-1 to +1) -0.7
whale_data_path Where whale snapshots are stored data/whale_data.json
regime_data_path Where market regime data is stored data/regime_data.json
sentiment_data_path Where sentiment data is stored data/sentiment_data.json

Multi-token portfolios

Trade several tokens with per-token allocation and strategies. Tokens are hot-reloadable — add or remove them in config.json and the change applies on the next cycle, no restart.

Portfolio weights

secondary_weights is a list parallel to secondary_mints — each entry is that token's share of total capital (cash plus all open positions). Weights are normalized to sum to 1 on load; an empty list means an equal split. A weight of 0 makes a token watch-only — it is analyzed but never bought.

"secondary_mints": ["So1111...2", "JUPyiwr...gBz"],
"secondary_mint_symbols": ["SOL", "JUP"],
"secondary_weights": [0.6, 0.4]

A buy deploys at most weight × total_capital, so each token gets its slice instead of the first signal taking everything.

Per-token strategies

token_strategies assigns a different strategy per token; tokens not listed use the global strategy.

"token_strategies": { "SOL": "default", "JUP": "momentum" }

Per-token exchanges

token_exchanges assigns a different candle-data exchange per token (any ccxt exchange id); tokens not listed use the global data_exchange. Use it when one exchange doesn't list all your tokens — e.g. POPCAT is missing on OKX but available on MEXC:

"data_exchange": "okx",
"token_exchanges": { "POPCAT": "mexc" }

Both settings hot-reload, but data_exchange itself is structural — changing it still requires a restart. Each exchange client is created once and reused; candle history is keyed by symbol, so switching a token's exchange starts fresh data for it.

Changing tokens while running

  • Adding a token is always safe — it starts fresh (new position CSV, no history).
  • Removing a token with an open position is refused: the change is rolled back and an error is logged, because a removed token stops being managed (no stop-loss, take-profit, or trailing stop). Close the position first.
  • When the token set changes, the P&L baseline is recaptured automatically so profit figures stay correct.

How it works

SolTrade runs a continuous loop:

  1. Fetch — the bot retrieves fresh prices and candlesticks for every configured token.
  2. Analyze — the active strategy computes indicators (EMA, RSI, and Bollinger Bands by default) and produces entry / exit signals.
  3. Act — buy signals open a position; sell signals, stop-losses, take-profits, and trailing stops close it. Every trade is routed through the Jupiter Swap API.
  4. Protect — open positions are tracked and persisted to disk, so a restart resumes where it left off.

Optional layers — whale tracking, a confluence sizing filter, market regime detection, and a sentiment circuit breaker — operate between the signal and the trade. See Advanced features for details.

Terminal UI

The bot runs inside a full-screen terminal UI:

Screen Key Shows
Dashboard 1 Live wallet balance, portfolio value, profit, and per-token indicators
Logs 2 Full log history (scroll with arrow keys, PgUp/PgDn, Home/End)
Help 3 Keybindings and general info

Tab cycles through the screens; q or Ctrl-C quits. The bot keeps trading in the background while you browse the UI.

Features

Feature What it does
Technical analysis EMA, RSI, and Bollinger Bands out of the box — pure Python, no C libraries
Multiple tokens Trade several tokens in the same loop
Position management Stop-loss, take-profit, and trailing stop on every position
Custom strategies Use your own strategy file
Whale tracking Watches configured wallets for accumulation or dumping
Confluence filter Sizes every trade from whale activity, market regime, and sentiment
Market regime Scales positions down in bearish markets (opt-in)
Sentiment breaker Pauses trading when social sentiment crashes (opt-in)

Advanced features

How whale tracking, the confluence filter, market regime, and sentiment work

Whale wallet tracking

The tracker polls the wallets listed in whale_wallets at the interval specified by whale_poll_interval_minutes and compares balances over 1-hour, 4-hour, and 24-hour windows:

Signal Meaning
ACCUMULATING Whales are net buying (>10% balance increase)
DUMPING Whales are net selling (>10% balance decrease)
NEUTRAL No significant movement
NO_DATA No wallets configured, or not enough snapshots yet
"whale_wallets": {
  "SOL": ["wallet_address_1", "wallet_address_2"]
}

Top token holders can be discovered with the built-in CLI. The command below lists the ten largest holders of SOL:

uv run -m sol_trade.whale_discovery So11111111111111111111111111111111111111112 10

Usage: uv run -m sol_trade.whale_discovery TOKEN_MINT [LIMIT]

Confluence filter

The filter combines the whale signal, market regime, and sentiment to determine position size:

TA Signal Whale Activity Action Position Size
BUY ACCUMULATING Full entry 100%
BUY NEUTRAL Half entry 50%
BUY DUMPING Skip 0%
SELL DUMPING Full exit 100%
SELL NEUTRAL Half exit 50%
SELL ACCUMULATING Partial exit 50%

[!NOTE] With no whale wallets configured (or while the tracker is still collecting snapshots), trades pass at full size. The matrix only applies once wallets are set up and at least two snapshots exist.

In bearish regimes, all position sizes drop an additional 50%. Protective exits (stop-loss, take-profit, trailing stop) always execute at 100%.

Market regime detection

The market is classified from the SOL/USDC daily trend (20-day SMA) and DEX volume, and entries are scaled accordingly:

Regime Condition Position Modifier
BULLISH Price above 20-day SMA + rising volume 1.0x
NEUTRAL Mixed signals 1.0x
BEARISH Price below 20-day SMA + falling volume 0.5x

This feature is enabled by setting "market_regime_enabled": true in config.json.

Sentiment circuit breaker

The breaker polls social sentiment from Reddit for the tracked tokens and pauses trading when sentiment collapses:

  • Token pause — a token is blocked when its score drops below sentiment_threshold.
  • Market crash — all new entries pause when every tracked token is below sentiment_crash_threshold.
  • Recovery — blocks expire automatically after sentiment_pause_hours.

This feature is enabled by setting "sentiment_enabled": true in config.json.

Custom strategies

Note

Strategy names must be a single word, lowercase — momentum, trendline, etc.

  1. Create strategies/{name}_strategy.py.
  2. Define a class {Name}Strategy(BaseStrategy) with the following methods:
    • __init__(self, df) — store self.df and set the risk parameters stoploss, takeprofit, trailing_stoploss, and trailing_stoploss_target (percentages).
    • apply_strategy(self) — compute indicators, then set self.df["entry"] = 1 on bars that should buy and self.df["exit"] = 1 on bars that should sell.
  3. Set "strategy": "{name}" in config.json.

Indicators (ema, sma, rsi) are available from sol_trade.strategy — pure-Python, TA-Lib-equivalent implementations.

Example — a momentum strategy
# strategies/momentum_strategy.py
import pandas as pd

from sol_trade.strategy import ema, rsi
from .base_strategy import BaseStrategy


class MomentumStrategy(BaseStrategy):
    def __init__(self, df: pd.DataFrame):
        self.df = df
        self.stoploss = 5
        self.takeprofit = 10
        self.trailing_stoploss = 2
        self.trailing_stoploss_target = 5

    def apply_strategy(self):
        self.df["ema_fast"] = ema(self.df["close"], 8)
        self.df["ema_slow"] = ema(self.df["close"], 21)
        self.df["rsi"] = rsi(self.df["close"], 14)

        entry = (self.df["ema_fast"] > self.df["ema_slow"]) & (self.df["rsi"] <= 40)
        exit_ = (self.df["ema_fast"] < self.df["ema_slow"]) | (self.df["rsi"] >= 70)

        self.df.loc[entry, "entry"] = 1
        self.df.loc[exit_, "exit"] = 1

        return self.df

[!IMPORTANT] The loader already picks the class for the configured strategy name, so apply_strategy must not be gated on config().strategy — a guard like if config().strategy == "momentum" silently no-ops when the strategy is assigned per-token via token_strategies.

New strategies may be contributed via pull request.

FAQ

What happens if I stop the bot while I'm holding a position? Your open position is saved to data/{TOKEN}_data.csv. On restart, the bot resumes managing its stop-loss and take-profit.

Do I need a Jupiter API key? No. It is optional and only sent if set — the default swap/v2 endpoint works without one.

Can I trade more than one token? Yes. Add each token to secondary_mints (and its symbol to secondary_mint_symbols) and SolTrade trades them all in the same loop.

Where is my private key stored? Only in the .env file on your machine. The bot loads and signs locally — it is never sent to any server, and .env is git-ignored.

Glossary

Term Meaning
Primary mint The token you trade with, usually a stablecoin like USDC
Secondary mint The token you trade for, e.g. SOL
Trading interval Minutes between each technical analysis pass
Price update interval Seconds between price refreshes
Slippage Difference between expected and executed trade price
BPS Basis points — 100 BPS = 1%
Whale A wallet holding a large amount of a token

About

A Solana trading bot with lots of features.

Topics

Resources

Stars

3 stars

Watchers

1 watching

Forks

Releases

Used by

Contributors

Languages