SuperAPI AI Directory

aicoin-freqtrade

Testing & quality

View repository

Updated 10-13 21:13

About this skill

AiCoin Freqtrade

Freqtrade strategy / backtesting / deployment / live control — automatic adaptation across the three CoinClaw engines.

Key principles (read before acting)

1. In the CoinClaw container, freqtrade is a resident daemon

All three engine containers — OpenClaw / Hermes / Claude Code — start freqtrade as a resident process via supervisord, listening on 127.0.0.1:8080, by default running NoOpStrategy(dry run). Do not start your own freqtrade process — it will fight with the daemon over the port, and the dashboard will immediately go offline.

The correct flow is: write the strategy file → call ft-deploy.mjs deploy {"strategy":"..."} → the script modifies the config + restarts the daemon. The dashboard will automatically show the new strategy.

scripts/ft.mjs + scripts/ft-deploy.mjs Built-in three-engine auto-detection (lib/coinclaw-env.mjs), paths / auth / supervisord socket are all resolved automatically, the agent does not need to care which engine it is.

2. Always call the freqtrade REST API first, do not "calculate it yourself"

When the user asks you must first call
how much have I earned now / total P&L / how much has it gone up today ft.mjs profit (/api/v1/profit)
positions / what is currently open ft.mjs trades_open (/api/v1/status)
balance / how much capital ft.mjs balance (/api/v1/balance)
what strategy is running / current mode ft.mjs daemon_info or config
trade history / closed trades ft.mjs trades_history
per-pair performance ft.mjs profit_per_pair

Dashboard number alignment rules (critical): when the user asks "how much have I earned" you must reporttwo numbers:

  • Cumulative realized P&L of closed trades = profit_closed_coin (USDT) — The cumulative P&L in the dashboard top bar = this
  • Total P&L including floating = profit_all_coin (USDT) — realized closed trades + floating P&L of current positions

Calling only /status to get the floating P&L of positions will miss the realized portion, causing inconsistency with the dashboard numbers — the user will notice immediately and trust drops to zero.

3. Switching strategy / switching to live / switching pairs must go through the script

config.json is read once when the daemon starts; manual edits will not take effect automatically. You must use:

Operation Command Whether a daemon restart is needed
Switch strategy ft.mjs set_strategy {"strategy":"X"} Restart required (~30s)
Switch pairs ft.mjs set_pairs {"pairs":[...]} No restart, reload_config is enough
Switch to live/dry run ft.mjs set_dry_run {"dry_run":false} Restart required
reload config ft.mjs reload No restart

Or complete all changes at once: ft-deploy.mjs deploy {"strategy":"X","pairs":["BTC/USDT:USDT"],"dry_run":false}.

For any operation that directly modifies config.json (including manually editing pair_whitelist / minimal_roi / stoploss, etc.), you must immediately call ft.mjs reload afterward — otherwise the daemon is still running with the old config in memory, and changes such as whitelist/stoploss will not take effect. Forgetting to reload is the most common reason for "I changed it but it didn't work".

High-stake operations initiated proactively in chat must have a strong confirm(violation is an error):

Applies to: the user saying in chat "close it", "switch to live", "sell", "open a position", etc. — operations that call force_exit / force_enter / set_dry_run through the agent.

Flow:

  1. List a preview first: which trade / pair is affected / current P&L / estimated profit or loss / dry_run vs live / balance status
  2. Explicitly wait for the user to type "confirm" or "yes" before actually calling force_exit / set_dry_run / force_enter
  3. Even if the user's tone is certain ("close it", "just switch"), you must still preview and wait for confirmation

What does not need confirm:

  • Query class (check positions / P&L / status) — read directly
  • The freqtrade daemon itself automatically opens/closes positions based on strategy signals — This is the daemon's own job; the user authorized it the moment they went live. The agent is not in this loop; don't block it and no confirm is needed
  • Non-destructive config (set_pairs add pairs,reload) — list the changes then execute directly

Counterexamples that violate the rules:

  • ❌ User says "close it", and you directly call force_exit and close a real position (a real bug caught by K-Live-3 dogfood)
  • ❌ User says "go live", and without listing the .env keys / balance / risk you directly set_dry_run {"dry_run":false}
  • ✅ User says "close it", and you list "Position: BNB/USDT 0.05 +$1.07, close this order? dry_run=true paper trading" and wait for user confirmation

Writing a strategy + switching strategy tends to be split into two rounds(one round for create_strategy, one round for set_strategy). Not a technical limitation, it's a UX choice:

  1. First round: finish writing the strategy file → tell the user "X.py has been generated, switch to it?"
  2. After the user confirms, second round: set_strategy switch strategy + restart daemon (~30s)

This way the user can review the generated file before switching strategies; during the 30s daemon restart the user has expectations about the state and won't misjudge the chat as frozen. If the user explicitly says "do it all in one go", you can run both steps in a single turn, but by default split into rounds.

4. Freqtrade does not support grid strategies (grid)

When the user asks about grid, directly state the limitation + suggest trend following / range strategy / grid return simulator as alternatives. Don't force-write a fake grid.

Quick reference

Task Command
Check daemon status + config node scripts/ft-deploy.mjs check or ft.mjs daemon_info
Check strategy list node scripts/ft-deploy.mjs strategy_list
Create strategy (quick generator) node scripts/ft-deploy.mjs create_strategy '{"name":"MyStrat","timeframe":"15m","indicators":["rsi","macd","ema"],"aicoin_data":["funding_rate"]}'
Deploy strategy to daemon node scripts/ft-deploy.mjs deploy '{"strategy":"MyStrat"}'
Deploy + go live node scripts/ft-deploy.mjs deploy '{"strategy":"MyStrat","dry_run":false}'
Backtest node scripts/ft-deploy.mjs backtest '{"strategy":"MyStrat","timeframe":"1h","timerange":"20250101-20260301"}'
Hyperopt parameter tuning node scripts/ft-deploy.mjs hyperopt '{"strategy":"MyStrat","timeframe":"1h","epochs":100}'
Check P&L node scripts/ft.mjs profit
Check positions node scripts/ft.mjs trades_open
Check balance node scripts/ft.mjs balance
Switch trading pair node scripts/ft.mjs set_pairs '{"pairs":["BTC/USDT:USDT","ETH/USDT:USDT"]}'
Restart daemon node scripts/ft.mjs restart
Check logs node scripts/ft-deploy.mjs logs '{"lines":100}'

Creating a strategy: first decide which path to take

Decision rules:

  • The user only gave a vague description ("RSI strategy", "MA crossover", "Bollinger band reversion") and did not specify concrete parameter details → A. Quick generator
  • The user gave concrete logic (custom entry/exit conditions, multi-timeframe confluence, multi-coin rotation, composite indicators, custom position sizing) → B. Write Python directly
  • After generating with A, the user wants to change details → directly edit the generated .py file, don't re-run create_strategy to overwrite it

A. Quick generator (simple strategies)

create_strategy One command generates a runnable strategy file. Suitable for "get it running first, then tune" scenarios:

node scripts/ft-deploy.mjs create_strategy '{"name":"MACDStrategy","timeframe":"15m","indicators":["macd","rsi","atr"]}'
node scripts/ft-deploy.mjs create_strategy '{"name":"RSILong","timeframe":"1h","indicators":["rsi"],"direction":"long"}'
node scripts/ft-deploy.mjs create_strategy '{"name":"WhaleStrat","timeframe":"15m","indicators":["rsi","macd"],"aicoin_data":["funding_rate","ls_ratio"]}'

Optional indicators: rsi, bb, ema, sma, macd, stochastic/kdj, atr, adx, cci, williams_r, vwap, ichimoku, volume_sma, obv.

Optional direction: "long" (default, long only) | "short" (short only) | "both" (both directions). User says "buy when RSI<30, sell when RSI>70" → direction="long"(RSI>70 = close long, not open short). Only use "both" or "short"。

Optional aicoin_data: funding_rate、ls_ratio、big_orders、liquidation_map(both require a paid plan),open_interest(The v3 aggregated OI history is not yet connected, so it will automatically fall back to the default value).

Limitations of the generator: It can only combine preset indicators and does not support cross-timeframe, multi-coin rotation, or custom composite indicators. For these needs, go directly with B.

B. Custom Python strategy (complex logic)

Directly write the .py file to the daemon's strategy directory. This route can implement any strategy logic supported by freqtrade (cross-timeframe informative pairs, custom position management, multi-indicator composite conditions, etc.).

The three engines have different directories, get from daemon_info or useor use the Write tool to write to any of the paths below (the script will automatically use /api/v1/show_config to validate):

Engine strategy directory
OpenClaw ~/.openclaw/workspace/strategies/
Hermes /workspace/strategies/
Claude Code /workspace/strategies/

Use the AiCoin Python SDK (aicoin_data.py, already copied to the above directory during image build, and also copied as a fallback by create_strategy ):

It encapsulates the AiCoin Open API v3:

from aicoin_data import AiCoinData

ac = AiCoinData(cache_ttl=300)   # 自动从 .env 读 key,内置 5 分钟缓存

# 高层信号 —— 直接返回能用的数字,丢进策略即可
ac.whale_signal("BTC/USDT:USDT", "binance")        # 大单买卖压力 -1..+1
ac.ls_ratio_norm()                                 # 多空比 0..1(>0.5 偏多)
ac.funding_rate_pct("BTC/USDT:USDT", "binance")    # 最新资金费率(百分比)
ac.liq_bias("BTC/USDT:USDT", "binance")            # 清算图方向偏向 -1..+1

# 原始数据
ac.coin_ticker("bitcoin,ethereum")                 # 实时行情
ac.klines("BTC/USDT", "binance", interval="1h", limit=100)

# 任意 v3 接口 —— path 是 /api/v3/ 后那段,清单见 https://open.aicoin.com/api/v3/_catalog
ac.get("markets/hot-coins", {"tab_key": "defi"})
ac.get("hyperliquid/whales/open-positions", {"coin": "BTC"})

During backtesting, AiCoin real-time data is unavailable, and high-level signals will throw exceptions — the strategy must try/except fall back to default values. Funding rates, large orders, liquidations, etc. require a paid plan; without permission they will also throw exceptions (also fall back).

Complete template

from freqtrade.strategy import IStrategy, IntParameter, DecimalParameter
from pandas import DataFrame
import logging, time

logger = logging.getLogger(__name__)


class MyStrategy(IStrategy):
    INTERFACE_VERSION = 3
    timeframe = '15m'
    can_short = True

    minimal_roi = {"0": 0.05, "60": 0.03, "120": 0.01}
    stoploss = -0.05
    trailing_stop = True
    trailing_stop_positive = 0.02
    trailing_stop_positive_offset = 0.03

    rsi_buy = IntParameter(20, 40, default=30, space='buy')
    rsi_sell = IntParameter(60, 80, default=70, space='sell')

    _ac_funding_rate = 0.0
    _ac_last_update = 0.0

    def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        # RSI
        delta = dataframe['close'].diff()
        gain = delta.clip(lower=0).rolling(window=14).mean()
        loss = (-delta.clip(upper=0)).rolling(window=14).mean()
        rs = gain / loss
        dataframe['rsi'] = 100 - (100 / (1 + rs))

        # AiCoin 数据 (live/dry_run only, backtest 用默认值 0.0)
        dataframe['funding_rate'] = 0.0
        if self.dp and self.dp.runmode.value in ('live', 'dry_run'):
            now = time.time()
            if now - self._ac_last_update > 300:
                self._update_aicoin_data(metadata)
                self._ac_last_update = now
            dataframe.iloc[-1, dataframe.columns.get_loc('funding_rate')] = self._ac_funding_rate

        return dataframe

    def _update_aicoin_data(self, metadata: dict):
        try:
            import sys, os
            _sd = os.path.dirname(os.path.abspath(__file__))
            if _sd not in sys.path:
                sys.path.insert(0, _sd)
            from aicoin_data import AiCoinData
            ac = AiCoinData(cache_ttl=300)
            pair = metadata.get('pair', 'BTC/USDT:USDT')
            exchange = self.config.get('exchange', {}).get('name', 'binance')
            self._ac_funding_rate = ac.funding_rate_pct(pair, exchange)
        except Exception as e:
            logger.warning(f"AiCoin data error: {e}")

    def populate_entry_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[
            (dataframe['rsi'] < self.rsi_buy.value) &
            (dataframe['volume'] > 0),
            'enter_long'] = 1
        dataframe.loc[
            (dataframe['rsi'] > self.rsi_sell.value) &
            (dataframe['volume'] > 0),
            'enter_short'] = 1
        return dataframe

    def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
        dataframe.loc[(dataframe['rsi'] > 70), 'exit_long'] = 1
        dataframe.loc[(dataframe['rsi'] < 30), 'exit_short'] = 1
        return dataframe

After writing, use deploy {"strategy":"MyStrategy"} to make the daemon switch to this strategy.

AiCoin data integration mode

AiCoin data signal logic plan
funding_rate greater than 0.01% → long overcrowded → short signal; less than -0.01% → long signal Basic version
ls_ratio less than 0.45 (more shorts) → contrarian long; greater than 0.55 → contrarian short Basic version
big_orders (buy_vol-sell_vol)/total > 0.3 → whales buying, go long Standard version
open_interest OI up + price up = healthy trend; OI up + price down = reversal Professional version
liquidation_map more liquidations above → long squeeze → go long Advanced version

Backtesting notes

AiCoin real-time datanot available within the historical range. During backtesting:

  • AiCoin columns use default values (funding_rate=0.0, ls_ratio=0.5, whale_signal=0.0)
  • Backtest results only reflect thetechnical indicatorspart
  • only when running live/dry_run is real AiCoin data used, and performance should be better than in backtesting

When reporting backtest results to the user, this point must be proactively explained so the user does not think the backtest includes AiCoin signals.

Script API

scripts/ft-deploy.mjs — strategy / backtesting / deployment

Action parameter example
check (none) — returns daemon status + config + balance
daemon_info(in ft.mjs) (none) — get everything in a single call
deploy {"strategy":"MyStrat"} or {"strategy":"MyStrat","dry_run":false,"pairs":["BTC/USDT:USDT"]}
create_strategy {"name":"MyStrat","timeframe":"15m","indicators":["rsi","macd"],"direction":"long","aicoin_data":["funding_rate"]}
backtest {"strategy":"MyStrat","timeframe":"1h","timerange":"20250101-20260301","pairs":["ETH/USDT:USDT"]}
hyperopt {"strategy":"MyStrat","timeframe":"1h","epochs":100}
download_data {"timeframe":"1h","timerange":"20250101-"}
strategy_list (none)
backtest_results (none) — lists the filenames of the most recent 10 backtest results
start / stop (none) — in coinclaw mode calls supervisorctl; in host mode manages the PID
status / logs {"lines":100}
update / remove in coinclaw mode it is a no-op (prompts to use helm upgrade / web UI to delete the instance)

scripts/ft.mjs — real-time control (REST + config changes)

Action Purpose
daemon_info get strategy / mode / pairs / number of open trades in one go
profit cumulative closed positions + total P&L including floating (must call first when answering P&L-related questions)
trades_open current positions (call /status)
trades_history Closed trades
balance Balance
profit_per_pair Performance per trading pair
daily / weekly / monthly Time-dimension statistics
force_enter / force_exit Manual open/close position
set_strategy Switch strategy (modify config + restart daemon)
set_pairs Change trading pair whitelist (reload, no restart)
set_dry_run Switch to live/dry-run (modify config + restart daemon)
restart Restart freqtrade daemon (supervisorctl + kill as fallback)
reload reload_config without restarting
start / stop / ping / version / health Standard REST
logs freqtrade built-in /logs endpoint

scripts/ft-dev.mjs — Debug (backtesting / candles / strategy details)

backtest_start / backtest_status / backtest_history / candles_live / candles_analyzed / strategy_list / strategy_get / whitelist / blacklist etc.

Environment variables and authentication

.env Automatic loading order:

  • Inside the coinclaw container: /workspace/.env (Hermes/CC) or /home/node/.openclaw/workspace/.env (OpenClaw)
  • host mode: ~/.coinos/.env(coinos folder, recommended) → current directory .env → old ~/.openclaw/.env(backward compatible)

freqtrade REST authentication: freqtrade-api.mjs automatically reads the password from the .ft_api_pass file inside the container, the agent does not need to configure FREQTRADE_USERNAME / FREQTRADE_PASSWORD. Users can also override via .env .

Exchange keys are configured in the EnvSection of the web UI, and after writing to .env, the entrypoint will automatically patch them into freqtrade config.json. the agent must not directly read .env to show the user the exchange keys.

AiCoin Open API key (used for strategies integrating AiCoin data):

AICOIN_ACCESS_KEY_ID=your-key-id
AICOIN_ACCESS_SECRET=your-secret

Paid feature guidance

When returning 304 / 403, do not retry, directly tell the user:

Plan Price Purpose
Free $0 Pure technical indicators
Basic $29/mo + funding_rate, ls_ratio
Standard $79/mo + big_orders, agg_trades
Advanced $299/mo + liquidation_map
Professional $699/mo + open_interest, ai_analysis

Obtaining address: https://www.aicoin.com/opendata

Cross-skill references

When the user asks Use
Simply querying market data / K-lines / news / funding rates (without opening positions) aicoin-market
Directly placing orders / opening positions / closing positions (not via freqtrade) aicoin-trading
Hyperliquid whales / positions / liquidations aicoin-hyperliquid
On-chain DEX swap / wallet balance / gas aicoin-onchain
Balance / positions / registration / API key configuration (account-related) aicoin-account

Common pitfalls

  • Do not cat /workspace/.ft_api_pass paste the internal daemon password into chat — directly use ft.mjs to call REST; the script reads the password internally and will not leak it.
  • Do not echo the user's exchange key in chat — these are highly sensitive data; guide the user to configure them in EnvSection.
  • Do not calculate RSI / MACD / EMA in your head — the values calculated by freqtrade will differ from your mental calculation; use ft-dev.mjs candles_analyzed to get the daemon's real indicators.
  • Do not "first stop daemon then freqtrade trade ... &" — that bypasses supervisord, and the next time the dashboard will still show the old daemon status. You must use set_strategy / deploy / restart.
  • It is normal that backtesting cannot obtain AiCoin data — do not fake CSV to feed backtesting; directly tell the user that backtesting only reflects technical indicators. See the "Data Accuracy Iron Rules" in the root AGENTS.md.