• bitcoinBitcoin(BTC)$79,804.00-0.73%
  • ethereumEthereum(ETH)$2,305.62-1.64%
  • tetherTether(USDT)$1.00-0.03%
  • binancecoinBNB(BNB)$740.610.14%
  • rippleXRP(XRP)$1.29-0.78%
  • usd-coinUSDC(USDC)$1.00-0.03%
  • solanaSolana(SOL)$100.25-1.27%
  • tronTRON(TRX)$0.329881-0.11%
  • Figure HelocFigure Heloc(FIGR_HELOC)$1.040.00%
  • zcashZcash(ZEC)$1,222.470.62%
  • HyperliquidHyperliquid(HYPE)$77.670.75%
  • dogecoinDogecoin(DOGE)$0.077099-0.07%
  • moneroMonero(XMR)$593.47-1.09%
  • USDSUSDS(USDS)$1.00-0.02%
  • whitebitWhiteBIT Coin(WBT)$76.62-1.17%
  • chainlinkChainlink(LINK)$11.34-1.16%
  • leo-tokenLEO Token(LEO)$9.030.21%
  • cardanoCardano(ADA)$0.204507-0.40%
  • stellarStellar(XLM)$0.167861-0.43%
  • nearNEAR Protocol(NEAR)$4.46-2.19%
  • bitcoin-cashBitcoin Cash(BCH)$277.96-0.59%
  • RainRain(RAIN)$0.007594-11.34%
  • Ethena USDeEthena USDe(USDE)$1.00-0.08%
  • daiDai(DAI)$1.00-0.02%
  • uniswapUniswap(UNI)$7.071.55%
  • litecoinLitecoin(LTC)$56.45-0.91%
  • avalanche-2Avalanche(AVAX)$8.89-0.57%
  • CantonCanton(CC)$0.098693-3.25%
  • USD1USD1(USD1)$1.00-0.02%
  • suiSui(SUI)$0.91-1.73%
  • the-open-networkGram (prev. Toncoin)(GRAM)$1.21-3.78%
  • hedera-hashgraphHedera(HBAR)$0.076425-1.23%
  • tether-goldTether Gold(XAUT)$4,014.150.47%
  • Global DollarGlobal Dollar(USDG)$1.00-0.02%
  • paypal-usdPayPal USD(PYUSD)$1.00-0.04%
  • quant-networkQuant(QNT)$202.365.69%
  • shiba-inuShiba Inu(SHIB)$0.000005-0.44%
  • BittensorBittensor(TAO)$250.05-1.39%
  • EthenaEthena(ENA)$0.2071680.47%
  • crypto-com-chainCronos(CRO)$0.053139-2.37%
  • Ripple USDRipple USD(RLUSD)$1.00-0.01%
  • Circle USYCCircle USYC(USYC)$1.140.01%
  • Ondo US Dollar YieldOndo US Dollar Yield(USDY)$1.15-0.06%
  • okbOKB(OKB)$108.810.76%
  • BlackRock USD Institutional Digital Liquidity FundBlackRock USD Institutional Digital Liquidity Fund(BUIDL)$1.000.00%
  • MemeCoreMemeCore(M)$0.951.55%
  • aaveAave(AAVE)$134.62-2.11%
  • OndoOndo(ONDO)$0.4123771.41%
  • Pump.funPump.fun(PUMP)$0.004208-1.62%
  • Falcon USDFalcon USD(USDF)$0.99-0.02%
TradePoint.io
  • Main
  • AI & Technology
  • Stock Charts
  • Market & News
  • Business
  • Finance Tips
  • Trade Tube
  • Blog
  • Shop
No Result
View All Result
TradePoint.io
No Result
View All Result

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

August 11, 2026
in AI & Technology
Reading Time: 4 mins read
A A
Building and Validating a Quantitative Trading Strategy with OctoBot, Walk-Forward Backtesting, Parameter Optimization, and Interactive Analysis
ShareShareShareShareShare
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"]1786462659
       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 SEARCH\n" + "=" * 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 evaluate\n", 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)

Credit: Source link

YOU MAY ALSO LIKE

Apple Adds More Siri AI Tools In Its 27.2 System Updates, Out Today

Apple Expands Siri AI Beyond English with 27.2 Software Updates – Unite.AI

ShareTweetSendSharePin

Related Posts

Apple Adds More Siri AI Tools In Its 27.2 System Updates, Out Today
AI & Technology

Apple Adds More Siri AI Tools In Its 27.2 System Updates, Out Today

October 26, 2026
Apple Expands Siri AI Beyond English with 27.2 Software Updates – Unite.AI
AI & Technology

Apple Expands Siri AI Beyond English with 27.2 Software Updates – Unite.AI

October 26, 2026
Instagram Wants You To Watch 90-Minute Videos On Your TV
AI & Technology

Instagram Wants You To Watch 90-Minute Videos On Your TV

October 26, 2026
Apollo-Led  Billion GPU Lease Deal Draws Binding SpaceX Pledge – Unite.AI
AI & Technology

Apollo-Led $40 Billion GPU Lease Deal Draws Binding SpaceX Pledge – Unite.AI

October 26, 2026
Next Post
Breaking: Trump’s Iran Statement Sends Markets Into Chaos

Breaking: Trump's Iran Statement Sends Markets Into Chaos

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Search

No Result
View All Result
FBI officers on the scene at Oregon elementary school following shooting

FBI officers on the scene at Oregon elementary school following shooting

October 24, 2026
U.S. taking Putin’s side would be a ‘historic mistake,’ Zelenskyy says

U.S. taking Putin’s side would be a ‘historic mistake,’ Zelenskyy says

October 23, 2026
Greece Enacts Social Media Ban For Kids Under 15

Greece Enacts Social Media Ban For Kids Under 15

October 23, 2026

About

Learn more

Our Services

Legal

Privacy Policy

Terms of Use

Bloggers

Learn more

Article Links

Contact

Advertise

Ask us anything

©2020- TradePoint.io - All rights reserved!

Tradepoint.io, being just a publishing and technology platform, is not a registered broker-dealer or investment adviser. So we do not provide investment advice. Rather, brokerage services are provided to clients of Tradepoint.io by independent SEC-registered broker-dealers and members of FINRA/SIPC. Every form of investing carries some risk and past performance is not a guarantee of future results. “Tradepoint.io“, “Instant Investing” and “My Trading Tools” are registered trademarks of Apperbuild, LLC.

This website is operated by Apperbuild, LLC. We have no link to any brokerage firm and we do not provide investment advice. Every information and resource we provide is solely for the education of our readers. © 2020 Apperbuild, LLC. All rights reserved.

No Result
View All Result
  • Main
  • AI & Technology
  • Stock Charts
  • Market & News
  • Business
  • Finance Tips
  • Trade Tube
  • Blog
  • Shop

© 2023 - TradePoint.io - All Rights Reserved!