msaltnet
smtm
Python

It's a game to get money

Last updated Jun 17, 2026
100
Stars
101
Forks
0
Issues
0
Stars/day
Attention Score
49
Language breakdown
No language data available.
β–Έ Files click to expand
README

smtm

build status license language codecov
It's a game to get money.

An LLM-powered autonomous cryptocurrency trading system made in Python. https://smtm.msalt.net

ν•œκ΅­μ–΄ πŸ‘ˆ

icon</em>wide_gold

A chat-driven LLM agent orchestrates the system -- registering accounts, managing profiles, and starting/stopping one or more trading sessions in parallel -- while each session runs its own separate fixed-interval loop that executes the actual trades.

  • SystemOperator (the chat agent) manages sessions via tools -- create/start/stop/compare -- and still supports the legacy single-session select/start/stop flow
  • Each session's TradingOperator runs a fixed-interval loop: DataProvider -> Strategy -> SafetyGuard -> Trader -> Analyzer
  • Strategy is pluggable -- algorithmic (Buy & Hold, RSI, SMA) or a single LLM judgment per tick (LLM)
  • SafetyGuard enforces trading limits before every order (with an account-level guard across sessions sharing an account); SystemMonitor independently logs all activity, tagged by session

Features

  • Chat-based orchestration agent: register accounts, manage profiles, and create/start/stop parallel trading sessions
  • Pluggable trading strategies executed on a fixed interval: Buy & Hold, RSI, SMA, or a single LLM judgment per tick (LLM)
  • Safety guardrails (max trade amount, daily trade limit, loss ratio ceiling)
  • CLI interactive mode and Telegram chatbot control
  • Strategy knowledge loaded as documents (SMA, RSI, Buy & Hold)
  • Pluggable LLM client interface β€” Claude is implemented; OpenAI / Ollama adapters are planned

Setup

Prerequisites

  • Python 3.9 or higher

Installation

git clone https://github.com/msaltnet/smtm.git
cd smtm
pip install -r requirements.txt

Environment Variables

Create a .env file in the project root (or export variables directly):

# Required: LLM API key (currently Anthropic Claude β€” the only implemented vendor)
SMTMLLMAPIKEY=youranthropicapikey

Upbit exchange (when using --exchange UPB)

UPBITOPENAPIACCESSKEY=yourupbitaccess_key UPBITOPENAPISECRETKEY=yourupbitsecret_key UPBITOPENAPISERVERURL=https://api.upbit.com

Bithumb exchange (when using --exchange BTH)

BITHUMBAPIACCESSKEY=yourbithumbaccesskey BITHUMBAPISECRETKEY=yourbithumbsecretkey BITHUMBAPISERVER_URL=https://api.bithumb.com

Telegram (mode 1 only)

TELEGRAMBOTTOKEN=yourtelegrambot_token TELEGRAMCHATID=yourchatid

Usage

CLI Interactive Mode

python -m smtm --mode 0 --budget 500000 --currency BTC --exchange UPB

Chat with the LLM to control trading. Type messages to ask about the market, start/stop trading, or check portfolio status.

You can also move run options into a JSON config file:

python -m smtm --config config/virtual-upbit.json

Supported config keys are mode, budget, currency, exchange, virtual, paper, term, strategy, log, token, and chatid. virtual is the recommended key for virtual trading; paper remains supported as a compatibility alias. interval is accepted as an alias for term, and chat_id is accepted as an alias for chatid. CLI arguments override config-file values.

You can also pick a trading strategy directly, or load a saved account profile:

# Run with an algorithmic strategy (no LLM call in the trading loop)
python -m smtm --mode 0 --strategy RSI --virtual --budget 500000

Run with the LLM decision strategy

python -m smtm --mode 0 --strategy LLM --virtual

Run with a saved account profile (config/profiles/<name>.json)

python -m smtm --mode 0 --profile my-btc-virtual

Example chat session

λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš” (q: μ’…λ£Œ): Switch to the RSI strategy
[Agent β†’ select_strategy(RSI)]
Switched to the RSI strategy.

λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš” (q: μ’…λ£Œ): start μžλ™ λ§€λ§€κ°€ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€

λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš” (q: μ’…λ£Œ): Show my portfolio [Agent β†’ get_portfolio] Cash: 495,000 KRW Β· BTC: 0.0001 Β· Current value: 500,000 KRW (0.0%)

λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš” (q: μ’…λ£Œ): stop μžλ™ λ§€λ§€κ°€ μ€‘μ§€λ˜μ—ˆμŠ΅λ‹ˆλ‹€

Telegram Chatbot Mode

python -m smtm --mode 1 --token <telegramtoken> --chatid <chatid>

Control trading through Telegram messenger. All messages are forwarded to the LLM.

Options

| Option | Description | Default | |--------|-------------|---------| | --config | JSON config file path | None | | --mode | 0: CLI interactive, 1: Telegram chatbot | (shows help) | | --budget | Trading budget (KRW) | 500000 | | --currency | Trading currency (e.g. BTC, ETH) | BTC | | --exchange | Exchange code (UPB: Upbit, BTH: Bithumb) | UPB | | --strategy | Trading strategy code (BNH, RSI, SMA, LLM) | BNH | | --profile | Load a saved account profile from config/profiles/ | None | | --virtual / --paper | Virtual trading mode with real-time quotes and simulated balance | False | | --no-virtual / --no-paper | Disable virtual trading when the config file enables it | False | | --term | Trading tick interval in seconds | 60 | | --log | Log file name | None |

Virtual Trading

python -m smtm --mode 0 --budget 500000 --currency BTC --exchange UPB --virtual
python -m smtm --mode 0 --currency BTC --exchange UFC --virtual

Virtual trading keeps the selected DataProvider but routes orders to the in-memory SimulationTrader instead of a real exchange. Orders are not sent to the exchange; they are applied to a virtual account so portfolio value and returns can be inspected. Quotes are injected from the latest primary_candle close. State is in-memory only and commission is currently 0.

Supported Exchanges & Data Providers

--exchange selects both the market data source and the order-placing trader. End-to-end trading requires a matching entry in both factories. Any code in this table can be combined with --virtual to route orders through SimulationTrader instead of the real exchange.

| Code | Data Provider | Trader | Notes | |------|---------------|--------|-------| | UPB | Upbit | Upbit | Default | | BTH | Bithumb | Bithumb | | | BNC | Binance | β€” | Data only; no trader yet | | UBD | Upbit + Binance (merged) | β€” | Data only; no trader yet | | UPN | Upbit + Crypto News RSS (CoinDesk) | Upbit | Candle + text news items in one feed | | UMN | Upbit + Multi-source News (CoinDesk / CoinTelegraph / Decrypt / CryptoSlate) | Upbit | Candle + aggregated news from four sources | | USC | Upbit + Multi News + Reddit + Fear & Greed Index | Upbit | Full social/sentiment snapshot alongside candle | | UFC | Upbit + all public sources below (price / on-chain / funding / macro / notices / news / social / tech) | Upbit | Heaviest option β€” single "full context" feed per tick |

Registered in smtm/data/dataproviderfactory.py and smtm/trader/trader_factory.py.

Building-block signal providers (use directly, not factory-registered). All use free public APIs with no key required:

| Class | CODE | type emitted | Source | |-------|--------|----------------|--------| | NewsDataProvider | NWS | news | Generic RSS (CoinDesk by default) | | CoinTelegraphNewsDataProvider | CTN | news | cointelegraph.com/rss | | DecryptNewsDataProvider | DCN | news | decrypt.co/feed | | CryptoSlateNewsDataProvider | CSN | news | cryptoslate.com/feed/ | | BitcoinMagazineNewsDataProvider | BMN | news | bitcoinmagazine.com/.rss/full/ | | TheBlockNewsDataProvider | TBN | news | theblock.co/rss.xml β€” crypto/finance crossover | | WSJMarketsNewsDataProvider | WSJ | news | feeds.a.dj.com/rss/RSSMarketsMain.xml β€” WSJ Markets | | MarketWatchNewsDataProvider | MWN | news | feeds.marketwatch.com/marketwatch/topstories/ | | CNBCFinanceNewsDataProvider | CNB | news | cnbc.com/id/10000664/device/rss/rss.html β€” CNBC Markets | | MultiNewsDataProvider | MNS | news | Aggregates multiple news sources | | RedditDataProvider | RDT | reddit | Any subreddit Atom feed (/r/{sub}/.rss) | | CryptoCurrencyRedditDataProvider | RCC | reddit | r/CryptoCurrency | | BitcoinRedditDataProvider | RBT | reddit | r/Bitcoin | | FearGreedDataProvider | FGI | sentiment_index | api.alternative.me/fng/ (Crypto Fear & Greed) | | CoinGeckoDataProvider | CGK | price_snapshot | api.coingecko.com/api/v3/simple/price β€” price / market cap / 24h volume / 24h change | | CoinCapDataProvider | CCP | price_snapshot | api.coincap.io/v2/assets/{id} β€” alternative with higher rate limit | | CryptoGlobalDataProvider | CGL | crypto_global | api.coingecko.com/api/v3/global β€” total market cap / BTCΒ·ETHΒ·stablecoin dominance | | YahooFinanceDataProvider | YFN | macro_market | query1.finance.yahoo.com/v8/finance/chart β€” DXY / S&P500 / VIX / Gold / US10Y / Nasdaq | | BlockchainInfoDataProvider | BCI | onchain_stats | api.blockchain.info/stats β€” BTC hash rate / difficulty / tx / mempool | | MempoolFeesDataProvider | MPF | mempool_fees | mempool.space/api/v1/fees/recommended β€” BTC fee sat/vB | | EtherscanGasDataProvider | EGS | eth_gas | api.etherscan.io/api?module=gastracker&action=gasoracle β€” ETH gas safe/propose/fast (gwei), optional key | | BinanceFundingRateDataProvider | BFR | funding_rate | fapi.binance.com/fapi/v1/premiumIndex β€” perp funding / mark price | | BinanceOpenInterestDataProvider | BOI | open_interest | fapi.binance.com/futures/data/openInterestHist β€” perp open interest (contracts + USD notional) | | BinanceLongShortRatioDataProvider | BLS | longshortratio | fapi.binance.com/futures/data/globalLongShortAccountRatio β€” retail / top trader long vs short ratio | | UpbitNoticeDataProvider | UPT | notice | api-manager.upbit.com/api/v1/notices β€” Upbit announcements | | ExchangeRateDataProvider | FXR | exchange_rate | open.er-api.com/v6/latest/USD β€” USD β†’ KRW/JPY/EUR/CNY | | HackerNewsDataProvider | HNS | hackernews | hn.algolia.com/api/v1/searchbydate β€” crypto stories |

DataProvider.getinfo() may return a mixed list of typed dicts β€” numeric types such as primarycandle, binance, pricesnapshot, cryptoglobal, macromarket, onchainstats, mempoolfees, ethgas, fundingrate, openinterest, longshortratio, exchangerate, sentimentindex, and text / social types such as news, reddit, hackernews, notice. Each dict is self-describing via its type field; see smtm/data/data_provider.py for the contract and UpbitNewsDataProvider (UPN) / UpbitMultiNewsDataProvider (UMN) / UpbitSocialDataProvider (USC) / UpbitFullContextDataProvider (UFC) for working multi-type examples.

Safety Guardrails

SafetyGuard validates every trade tool call before execution and cannot be bypassed by the LLM. Defaults are defined in smtm/llm/safety_guard.py (SafetyConfig):

| Parameter | Description | Default | |-----------|-------------|---------| | maxtradeamount | Max KRW value of a single trade | 100,000 | | maxdailytrades | Max number of trades per calendar day | 20 | | maxlossratio | Cumulative loss floor (negative ratio) | -0.20 (-20%) | | initial_budget | Baseline for loss-ratio calculation | value of --budget |

To override the defaults, pass a safety entry into the operator config when wiring SystemOperator:

config = {
    "budget": 500000,
    "safety": {
        "maxtradeamount": 50000,
        "maxdailytrades": 10,
        "maxlossratio": -0.10,
    },
}
operator = SystemOperator(llm_client, config)

Testing

# Install dev dependencies
pip install -r requirements-dev.txt

Run all tests

python -m pytest tests/

Run by category

python -m pytest tests/unit_tests/ # Unit tests python -m pytest tests/e2e_tests/ # E2E tests python -m pytest tests/integration_tests/ # Integration tests (requires API keys)

Test Structure

| Directory | Description | External APIs | |-----------|-------------|---------------| | tests/unit_tests/ | Individual component tests | All mocked | | tests/e2e_tests/ | Full pipeline tests (chat β†’ tool β†’ trade β†’ result) | LLM, exchange, market data are Fake; all internal components run real code | | tests/integration_tests/ | Real exchange API tests | Requires API keys |

E2E Tests

E2E tests verify the complete flow without calling any external APIs. Only the system boundary is replaced with Fake implementations:

  • FakeLlmClient β€” Returns pre-scripted LLM responses (tool calls and text)
  • SimulationTrader β€” Production virtual-trading trader with real balance/asset state management
  • FakeDataProvider β€” Returns static market candle data
All internal components (SystemOperator, TradingOperator, ToolRouter, SafetyGuard, SystemMonitor, all Strategies and Tools) run with real code.

Architecture

The system is split into two layers, coordinated by a SessionManager that runs one or more sessions in parallel:

  • SystemOperator β€” chat-based LLM agent; orchestrates account registration, profiles, and session lifecycle via Tools (does not trade directly)
  • SessionManager β€” owns all TradingSessions (default session plus any created via chat); validates budgets against real account balances and prevents duplicate (account, symbol) allocations
  • TradingOperator β€” one per session; fixed-interval loop: DataProvider -> Strategy -> SafetyGuard -> Trader -> Analyzer
  • Strategy β€” pluggable: algorithmic (Buy & Hold, RSI, SMA) or a single LLM judgment per tick (LLM)
  • SystemMonitor β€” independently logs all activity (market data, requests, results, safety events, LLM usage), tagged by session

Multi-Session Parallel Trading

Run multiple strategies across accounts and symbols in parallel β€” all controlled by chatting with the agent:

  • Register accounts by env-var names (SMTMKEY1...), never raw keys
  • Create profiles (strategy Γ— exchange Γ— symbol Γ— budget Γ— account)
  • createsession / startsession / compare_performance via chat
  • Per-session budgets are validated against the real account balance,
and an account-level guard caps daily trades across sessions
  • Designed for a handful of concurrent sessions β€” each session polls the exchange independently, so keep session count modest to respect API rate limits
More information πŸ‘‰Wiki

Β© 2026 GitRepoTrend Β· msaltnet/smtm Β· Updated daily from GitHub