Skip to main content
NOVOSKY uses three configuration files. Never commit .env — it contains secrets.

Environment variables (.env)

Copy from the template:
cp .env.example .env
# MT5 API server
API_URL=http://<YOUR_MT5_SERVER_IP>:6542
API_TOKEN=<your_api_token>

# Cloudflare R2 — model storage (required for both push and pull)
CF_R2_ACCESS_KEY_ID=<your_r2_access_key_id>
CF_R2_SECRET_ACCESS_KEY=<your_r2_secret_access_key>

# Telegram (optional — leave blank to disable notifications)
TELEGRAM_TOKEN=<bot_token>
TELEGRAM_CHAT_ID=<chat_id>

# Supabase (optional — cloud trade dashboard)
SUPABASE_URL=https://...
SUPABASE_KEY=<service_key>
SUPABASE_BOT_NAME=novosky-vt
Getting Cloudflare R2 credentials: Go to dash.cloudflare.com → R2 → Manage R2 API tokens. Create a token with Object Read & Write on bucket novosky-models. Copy the Access Key ID and Secret Access Key into your .env.

Runtime config (config.json)

Controls strategy execution, risk management, and filters. All values are read at startup and at each main loop cycle.

Strategy

KeyDefaultDescription
strategyml_ensembleMust be ml_ensemble to use the ML brain.
account_typestandard"cent" or "standard". See Cent vs standard.

Dynamic SL/TP

KeyDefaultDescription
dynamic_sltp.enabledtrueUse ATR-based SL/TP. Must match the labeling method.
dynamic_sltp.sl_atr_multiplier0.8SL = ATR × this value.
dynamic_sltp.tp_atr_multiplier0.8TP = ATR × this value.
Keep dynamic_sltp.enabled = true at all times. The models were trained with ATR-aware labels. Disabling this causes live win rate to drop from ~78% to ~49%.

Position sizing

KeyDefaultDescription
dynamic_position_sizing.risk_percent2Percent of equity risked per trade.
dynamic_position_sizing.max_lot1.0Maximum lot size cap.

Risk filters

KeyDefaultDescription
max_consecutive_losses5Circuit breaker: stops trading after N losses in a row.
max_daily_loss99999Stop trading if daily loss exceeds this amount ($).
max_daily_profit99999Pause after hitting this daily profit ($).
trade_cooldown_seconds0Seconds to wait between trades. 0 = back-to-back OK.
news_block_minutes0Block trading N minutes before/after high-impact news.

Session and trend filters

KeyDefaultDescription
enable_ema_trend_filterfalseBUY only above EMA200, SELL only below EMA200.
timezone.offset_hours7Local UTC offset used for resets, summaries, and notifications. Examples: 7=WIB, 8=MYT/SGT, 5.5=IST.
The repo examples use WIB because the project started with an Indonesia-based trading schedule, but you can set your own local timezone in config.json.

ML position management

KeyDefaultDescription
ml_active_management.enabledtruePhase 8: position model monitors open trades.

ML config (ml_config.json)

Controls how models are trained and used at inference time. Most values here require a retrain if changed.

Labeling

KeyDefaultDescription
labeling.methodatr_awareLabel generation method. Always keep atr_aware.
labeling.atr_tp_mult1.5TP multiplier for label generation at training time.
labeling.atr_sl_mult0.8SL multiplier — must match config.json sl_atr_multiplier.
labeling.lookahead_candles48M15 bars to check for TP/SL hit (= 12 hours).

Signal model inference

KeyDefaultDescription
prediction.confidence_threshold0.60Min ensemble confidence to generate a trade signal.
prediction.min_probability_diff0.10Required gap between top-2 class probabilities.
risk_management.min_atr_threshold15Min ATR in $ — blocks trades in low-volatility markets.

Position model inference

KeyDefaultDescription
position_model.prediction.exit_threshold0.80Min EXIT probability to close early.
position_model.prediction.min_prob_diff0.25EXIT must beat HOLD by this margin.
position_model.prediction.add_threshold0.65Min ADD probability to pyramid.
The position model thresholds were tuned on 2026-04-15 via a 13-config sweep. At exit_threshold=0.80 and min_prob_diff=0.25, the model fires ~1 exit per 38 days, preserving TP-bound winners.

ADX gotcha

adx_14 in the feature set is normalized to 0–1, not the traditional 0–100 scale. This affects any ADX-related filters:
"adx_regime_filter": {
  "min_adx": 0.20   // This means traditional ADX 20 — NOT the value 20
}
Passing 20 instead of 0.20 would block all trades.