It's a game to get money
smtm
It's a game to get money.
An LLM-powered autonomous cryptocurrency trading system made in Python. https://smtm.msalt.net
νκ΅μ΄ π
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
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_performancevia chat- Per-session budgets are validated against the real account balance,
- Designed for a handful of concurrent sessions β each session polls the exchange independently, so keep session count modest to respect API rate limits