Close Menu
NCIJ Network NCIJ Network
    What's Hot

    Is regulation enough to stop AI systems killing us all? | AI (artificial intelligence)

    October 7, 2026

    PETA euthanized 67% of animals it took in during 2024, data shows

    October 7, 2026

    LA and San Diego residents ‘horrified’ by Trump suggesting Iran bomb them | Los Angeles

    October 6, 2026
    Facebook X (Twitter) Instagram
    Trending
    • Is regulation enough to stop AI systems killing us all? | AI (artificial intelligence)
    • PETA euthanized 67% of animals it took in during 2024, data shows
    • LA and San Diego residents ‘horrified’ by Trump suggesting Iran bomb them | Los Angeles
    • Latvia moves to expel Russian journalist who used to work for BBC – POLITICO
    • Ofcom investigates Meta over Instagram Instants feature
    • What exactly is ISOC? And what does it mean for you?
    • Google Launches Nano Banana 2.1: Better Than Its Predecessor at Half the Price
    • An extinct crocodilian was found among rocks that built the Great Sphinx
    • About
      • Our Team
      • Editorial Policy
      • Editorial Independence
      • International Support
    • Trust & Standards
      • AI Usage Policy
      • Conflict of Interest Policy
      • Corrections Policy
      • Ethics Policy
      • Fact-Checking Policy
      • Source Protection
    • Get Involved
      • Guide for Sources
      • Support Independent Journalism
    • Legal
      • Cookie Policy
      • Privacy Policy
      • Terms of Use
    Facebook X (Twitter) Instagram
    NCIJ Network NCIJ Network
    Wednesday, October 7
    • Home
    • World
    • Ai
    • Business
    • Politics
    • Health
    • Crypto
    • Science
    • Technology
    • Cybersecurity
    • Defense & Security
    • Economy
    • Energy
    • Europe
    • More
      • Fact Check
      • Investigations
      • Opinion & Analysis
      • Environment
    NCIJ Network NCIJ Network
    Home»Artificial Intelligence

    Building and Validating a Quantitative Trading Strategy with OctoBot, Walk-Forward Backtesting, Parameter Optimization, and Interactive Analysis

    NCIJ NETWNCIJ NETWORKBy NCIJ NETWNCIJ NETWORKAugust 11, 2026 Artificial Intelligence No Comments4 Mins Read
    Share
    Facebook Twitter LinkedIn Pinterest Email

    WORKER = os.path.join(WORK_DIR, "octobot_worker.py")
    WORKER_SRC = r'''
    import asyncio, itertools, json, os, sys, time, traceback
    import numpy as np
    import tulipy
    import octobot_script as obs
    CFG  = json.load(open(os.environ["OBS_CONFIG"]))
    OUT  = os.environ["OBS_OUT"]
    FIX  = CFG["fixed"]
    for kw in ("Close", "High", "Low", "Time", "market", "current_live_time", "plot_indicator"):
       if not hasattr(obs, kw):
           raise RuntimeError(
               f"octobot_script.{kw} missing -> tentacles are not installed. "
               "Run: python -m octobot_script.cli install_tentacles"
           )
    def tail(*arrays):
       """tulipy indicators return different lengths; right-align them all."""
       n = min(len(a) for a in arrays)
       return [np.asarray(a)[-n:] for a in arrays]
    def clamp(v):
       return float(min(max(v, FIX["min_offset_pct"]), FIX["max_offset_pct"]))
    def build_callbacks(params, run_data):
       """
       OctoBot-Script splits a strategy into:
         initialize(ctx) -> runs once on the first candle. Do vectorised work here.
         strategy(ctx)   -> runs on EVERY closed candle. Keep it cheap.
       """
       async def initialize(ctx):
           closes = await obs.Close(ctx, max_history=True)
           highs  = await obs.High(ctx,  max_history=True)
           lows   = await obs.Low(ctx,   max_history=True)
           times  = await obs.Time(ctx,  max_history=True, use_close_time=True)
           rsi  = tulipy.rsi(closes, period=params["rsi_period"])
           ema_f = tulipy.ema(closes, period=FIX["ema_fast"])
           ema_s = tulipy.ema(closes, period=FIX["ema_slow"])
           atr   = tulipy.atr(highs, lows, closes, period=FIX["atr_period"])
           t, c, rsi, ema_f, ema_s, atr = tail(times, closes, rsi, ema_f, ema_s, atr)
           atr_pct = np.where(c > 0, atr / c * 100.0, 0.0)
           entries, offsets = set(), {}
           for i in range(len(t)):
               oversold = rsi[i] < params["rsi_threshold"]
               uptrend  = ema_f[i] > ema_s[i]
               if oversold and uptrend and atr_pct[i] > 0:
                   ts = float(t[i])
                   entries.add(ts)
                   offsets[ts] = (
                       clamp(FIX["sl_atr_mult"]     * atr_pct[i]),
                       clamp(params["tp_atr_mult"]  * atr_pct[i]),
                   )
           run_data["entries"] = entries
           run_data["offsets"] = offsets
           if run_data.get("plot"):
               await obs.plot_indicator(ctx, f"RSI({params['rsi_period']})", t, rsi, entries)
               await obs.plot_indicator(ctx, f"EMA{FIX['ema_fast']}",  t, ema_f)
               await obs.plot_indicator(ctx, f"EMA{FIX['ema_slow']}",  t, ema_s)
               await obs.plot_indicator(ctx, "ATR %", t, atr_pct)
       async def strategy(ctx):
           now = obs.current_live_time(ctx)
           if now not in run_data["entries"]:
               return
           sl, tp = run_data["offsets"]1786470893
           await obs.market(
               ctx, "buy",
               amount=FIX["position_size"],
               stop_loss_offset=f"-{sl:.2f}%",
               take_profit_offset=f"{tp:.2f}%",
           )
       return initialize, strategy
    def metrics(res):
       br = res.report.get("bot_report", {})
       first = lambda d: float(list(d.values())[0]) if isinstance(d, dict) and d else float("nan")
       return {
           "profitability":  first(br.get("profitability", {})),
           "market":         first(br.get("market_average_profitability", {})),
           "reference":      br.get("reference_market"),
           "start_portfolio": str(br.get("starting_portfolio")),
           "end_portfolio":   str(br.get("end_portfolio")),
           "candles":        res.candles_count,
           "duration_s":     round(res.duration or 0, 2),
           "errors":         res.report.get("errors_count"),
       }
    async def load_data(window):
       """Try each exchange until one serves data (Binance blocks many datacenter IPs)."""
       start, end = window
       last = None
       for ex in CFG["exchanges"]:
           try:
               print(f"  ↓ fetching {CFG['symbol']} {CFG['time_frame']} from {ex} "
                     f"[{time.strftime('%Y-%m-%d', time.gmtime(start))} → "
                     f"{time.strftime('%Y-%m-%d', time.gmtime(end))}]", flush=True)
               data = await obs.get_data(
                   CFG["symbol"], CFG["time_frame"],
                   exchange=ex, exchange_type="spot",
                   start_timestamp=start, end_timestamp=end,
                   social_services=[],
               )
               print(f"    ✓ {ex} ok -> {data.data_files}", flush=True)
               return data, ex
           except Exception as e:
               last = e
               print(f"    ✗ {ex}: {type(e).__name__}: {e}", flush=True)
       raise RuntimeError(f"no exchange served data; last error: {last}")
    async def backtest(data, params, plot=False, storage=False):
       run_data = {"entries": None, "offsets": {}, "plot": plot}
       init_f, strat_f = build_callbacks(params, run_data)
       res = await obs.run(
           data, params,
           strategy_func=strat_f,
           initialize_func=init_f,
           enable_logs=False,
           enable_storage=storage,
       )
       return res, len(run_data["entries"] or ())
    async def main():
       out = {"grid": [], "best": None, "oos": None, "errors": []}
       print("n" + "=" * 78 + "n  IN-SAMPLE GRID SEARCHn" + "=" * 78, flush=True)
       is_data, ex_used = await load_data(CFG["in_sample"])
       out["exchange"] = ex_used
       keys  = list(CFG["grid"].keys())
       combos = [dict(zip(keys, v)) for v in itertools.product(*CFG["grid"].values())]
       print(f"  {len(combos)} configurations to evaluaten", flush=True)
       for i, params in enumerate(combos, 1):
           try:
               res, n_sig = await backtest(is_data, params)
               m = metrics(res)
               m.update(params); m["signals"] = n_sig
               m["edge"] = m["profitability"] - m["market"]
               out["grid"].append(m)
               print(f"  [{i:>2}/{len(combos)}] {params}  "
                     f"P&L {m['profitability']:+.2f}%  vs market {m['market']:+.2f}%  "
                     f"edge {m['edge']:+.2f}%  ({n_sig} signals, {m['duration_s']}s)", flush=True)
           except Exception as e:
               out["errors"].append(f"{params}: {e}")
               print(f"  [{i:>2}/{len(combos)}] {params} FAILED: {e}", flush=True)
               traceback.print_exc()
       await is_data.stop()
       if not out["grid"]:
           json.dump(out, open(OUT, "w")); raise SystemExit("no successful runs")
       best = max(out["grid"], key=lambda r: r["edge"])
       out["best"] = {k: best[k] for k in keys}
       print(f"n  🏆 best in-sample config: {out['best']}  (edge {best['edge']:+.2f}%)", flush=True)
       print("n" + "=" * 78 + "n  OUT-OF-SAMPLE VALIDATION (never optimised on)n" + "=" * 78,
             flush=True)
       oos_data, _ = await load_data(CFG["out_of_sample"])
       res, n_sig = await backtest(oos_data, out["best"], plot=True, storage=True)
       m = metrics(res); m.update(out["best"])
       m["signals"] = n_sig; m["edge"] = m["profitability"] - m["market"]
       out["oos"] = m
       print(f"  OOS P&L {m['profitability']:+.2f}%  vs market {m['market']:+.2f}%  "
             f"edge {m['edge']:+.2f}%  ({n_sig} signals)", flush=True)
       print("  " + res.describe(), flush=True)
       report_dir = os.path.join(os.getcwd(), "report")
       os.makedirs(report_dir, exist_ok=True)
       try:
           plot = await res.plot(report_file=os.path.join(report_dir, "report.html"), show=False)
           out["bundle"] = os.path.join(os.path.dirname(os.path.abspath(plot.report_file)),
                                        "report.json")
           print(f"  ✓ report bundle: {out['bundle']}", flush=True)
       except Exception as e:
           out["errors"].append(f"report: {e}")
           print(f"  ✗ report generation failed: {e}", flush=True)
       await oos_data.stop()
       json.dump(out, open(OUT, "w"), indent=2, default=str)
       print("n✓ results written to", OUT, flush=True)
    asyncio.run(main())
    '''
    with open(WORKER, "w") as f:
       f.write(WORKER_SRC)
    
    Analysis Backtesting building Interactive OctoBot Optimization Parameter Quantitative strategy Trading Validating WalkForward
    NCIJ NETWNCIJ NETWORK
    • Website

    Keep Reading

    Mistral AI Releases Mistral Large 4 (Le Chonk): A 1.05T Parameter Multimodal MoE Model

    Google DeepMind Releases EmbeddingGemma 2, a 740M Open Multimodal Embedding Model Built on Gemma 4

    Don Davis Bill Would Fine Candidates $10K for Trading on Their Own Elections

    Reka Releases Rho-1: A 19B Omni-Reasoning Model That Understands, Generates Video and Outputs Robot Actions in One

    Beyond Domain-Specific World Models: JEPA-Anything Uses 1 Recipe for 7 Fields

    Reflection AI Introduces Beam: A 501B Open-Weight MoE Model With 23B Active Parameters for Coding and Agentic Workloads

    Add A Comment
    Leave A Reply Cancel Reply

    Editors Picks

    Is regulation enough to stop AI systems killing us all? | AI (artificial intelligence)

    October 7, 2026

    PETA euthanized 67% of animals it took in during 2024, data shows

    October 7, 2026

    LA and San Diego residents ‘horrified’ by Trump suggesting Iran bomb them | Los Angeles

    October 6, 2026

    Latvia moves to expel Russian journalist who used to work for BBC – POLITICO

    October 6, 2026
    Latest Posts

    4 Best Compression Boots: Therabody, Hyperice, and More (2026)

    August 9, 2026

    Former Iraqi provincial governor arrested as graft crackdown continues | Corruption News

    August 9, 2026

    The culture surrounding ‘ideal’ childbirth has to evolve | Childbirth

    August 9, 2026

    Subscribe to News

    Get the latest sports news from NewsSite about world, sports and politics.

    NCIJ Network is an independent digital news platform delivering trusted investigative journalism, European and global news, in-depth analysis, and fact-based reporting with accuracy, transparency, and integrity.

    Facebook X (Twitter) Instagram Pinterest YouTube

    Is regulation enough to stop AI systems killing us all? | AI (artificial intelligence)

    October 7, 2026

    PETA euthanized 67% of animals it took in during 2024, data shows

    October 7, 2026

    LA and San Diego residents ‘horrified’ by Trump suggesting Iran bomb them | Los Angeles

    October 6, 2026

    Subscribe to Updates

    Get the latest creative news from FooBar about art, design and business.

    Type above and press Enter to search. Press Esc to cancel.