vortex-api 2.0.7__tar.gz → 2.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vortex_api-2.0.7/vortex_api.egg-info → vortex_api-2.1.1}/PKG-INFO +1 -1
- {vortex_api-2.0.7 → vortex_api-2.1.1}/pyproject.toml +2 -2
- vortex_api-2.1.1/vortex_api/__version__.py +1 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/vortex_api/api.py +82 -202
- vortex_api-2.1.1/vortex_api/backtest/__init__.py +117 -0
- vortex_api-2.1.1/vortex_api/backtest/_backtestingpy.py +287 -0
- vortex_api-2.1.1/vortex_api/backtest/_backtrader.py +464 -0
- vortex_api-2.1.1/vortex_api/backtest/_common.py +295 -0
- vortex_api-2.1.1/vortex_api/backtest/_vectorbt.py +377 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1/vortex_api.egg-info}/PKG-INFO +1 -1
- {vortex_api-2.0.7 → vortex_api-2.1.1}/vortex_api.egg-info/SOURCES.txt +6 -1
- vortex_api-2.0.7/vortex_api/__version__.py +0 -1
- {vortex_api-2.0.7 → vortex_api-2.1.1}/LICENSE +0 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/README.md +0 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/setup.cfg +0 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/vortex_api/__init__.py +0 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/vortex_api/vortex_feed.py +0 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/vortex_api.egg-info/dependency_links.txt +0 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/vortex_api.egg-info/requires.txt +0 -0
- {vortex_api-2.0.7 → vortex_api-2.1.1}/vortex_api.egg-info/top_level.txt +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "vortex_api"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.1.1"
|
|
8
8
|
description = "Vortex APIs to place orders in Rupeezy application"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -38,4 +38,4 @@ Homepage = "https://vortex.rupeezy.in"
|
|
|
38
38
|
Repository = "https://github.com/RupeezyTech/pyvortex"
|
|
39
39
|
|
|
40
40
|
[tool.setuptools.packages.find]
|
|
41
|
-
include = ["vortex_api"]
|
|
41
|
+
include = ["vortex_api", "vortex_api.*"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.1.1"
|
|
@@ -2,7 +2,6 @@ import requests
|
|
|
2
2
|
import csv
|
|
3
3
|
import datetime
|
|
4
4
|
import logging
|
|
5
|
-
import math
|
|
6
5
|
from enum import Enum
|
|
7
6
|
import inspect
|
|
8
7
|
import wrapt
|
|
@@ -658,9 +657,13 @@ class VortexAPI:
|
|
|
658
657
|
"""
|
|
659
658
|
Save backtest results to Rupeezy for viewing on the developer portal.
|
|
660
659
|
|
|
660
|
+
Supports multiple backtesting libraries (auto-detected from the result type):
|
|
661
|
+
- **backtesting.py**: pass the stats object from Backtest.run()
|
|
662
|
+
- **vectorbt**: pass a vbt.Portfolio object
|
|
663
|
+
- **backtrader**: pass the strategy from cerebro.run() (i.e. results[0])
|
|
664
|
+
|
|
661
665
|
Args:
|
|
662
|
-
stats: The
|
|
663
|
-
from the backtesting.py library.
|
|
666
|
+
stats: The result object from any supported backtesting library.
|
|
664
667
|
name (str): A label for this backtest run (e.g. "SMA Crossover v2").
|
|
665
668
|
symbol (str, optional): Primary instrument symbol.
|
|
666
669
|
description (str, optional): Notes about this run.
|
|
@@ -669,206 +672,83 @@ class VortexAPI:
|
|
|
669
672
|
Returns:
|
|
670
673
|
dict: { "status": "success", "backtest_id": "bt_abc123", "url": "https://..." }
|
|
671
674
|
"""
|
|
672
|
-
|
|
675
|
+
from .backtest import serialize_stats
|
|
676
|
+
payload = serialize_stats(stats, name, symbol, description, tags or [])
|
|
673
677
|
endpoint = "/strategies/backtests"
|
|
674
678
|
|
|
675
679
|
return self._make_api_request("POST", endpoint, data=payload)
|
|
676
680
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
"best_trade_pct": _sf(stats.get("Best Trade [%]")),
|
|
752
|
-
"worst_trade_pct": _sf(stats.get("Worst Trade [%]")),
|
|
753
|
-
"avg_trade_pct": _sf(stats.get("Avg. Trade [%]")),
|
|
754
|
-
"max_trade_duration_days": _dd(stats.get("Max. Trade Duration")),
|
|
755
|
-
"avg_trade_duration_days": _dd(stats.get("Avg. Trade Duration")),
|
|
756
|
-
"profit_factor": _sf(stats.get("Profit Factor")),
|
|
757
|
-
"expectancy_pct": _sf(stats.get("Expectancy [%]")),
|
|
758
|
-
"sqn": _sf(stats.get("SQN")),
|
|
759
|
-
"kelly_criterion": _sf(stats.get("Kelly Criterion")),
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
# --- Strategy name and parameters ---
|
|
763
|
-
strategy_name = "Unknown"
|
|
764
|
-
parameters = {}
|
|
765
|
-
strategy = stats.get("_strategy")
|
|
766
|
-
if strategy is not None:
|
|
767
|
-
strategy_class = strategy if isinstance(strategy, type) else strategy.__class__
|
|
768
|
-
strategy_name = strategy_class.__name__
|
|
769
|
-
for attr in vars(strategy_class):
|
|
770
|
-
if attr.startswith("_"):
|
|
771
|
-
continue
|
|
772
|
-
val = getattr(strategy_class, attr, None)
|
|
773
|
-
if callable(val):
|
|
774
|
-
continue
|
|
775
|
-
if isinstance(val, (int, float, str, bool)):
|
|
776
|
-
parameters[attr] = val
|
|
777
|
-
|
|
778
|
-
# --- Equity curve ---
|
|
779
|
-
equity_curve = []
|
|
780
|
-
ec = stats.get("_equity_curve")
|
|
781
|
-
if ec is not None and hasattr(ec, "index"):
|
|
782
|
-
equity_series = ec["Equity"]
|
|
783
|
-
step = max(1, len(equity_series) // 500)
|
|
784
|
-
for i in range(0, len(equity_series), step):
|
|
785
|
-
equity_curve.append({
|
|
786
|
-
"date": equity_series.index[i].strftime("%Y-%m-%d"),
|
|
787
|
-
"equity": round(float(equity_series.iloc[i]), 2),
|
|
788
|
-
})
|
|
789
|
-
if equity_curve and equity_curve[-1]["date"] != equity_series.index[-1].strftime("%Y-%m-%d"):
|
|
790
|
-
equity_curve.append({
|
|
791
|
-
"date": equity_series.index[-1].strftime("%Y-%m-%d"),
|
|
792
|
-
"equity": round(float(equity_series.iloc[-1]), 2),
|
|
793
|
-
})
|
|
794
|
-
|
|
795
|
-
# --- Drawdown curve (computed from equity peak) ---
|
|
796
|
-
drawdown_curve = []
|
|
797
|
-
if ec is not None and hasattr(ec, "index"):
|
|
798
|
-
equity_series = ec["Equity"]
|
|
799
|
-
running_max = equity_series.cummax()
|
|
800
|
-
drawdown = ((equity_series - running_max) / running_max) * 100
|
|
801
|
-
step = max(1, len(drawdown) // 500)
|
|
802
|
-
for i in range(0, len(drawdown), step):
|
|
803
|
-
dd_val = round(float(drawdown.iloc[i]), 4)
|
|
804
|
-
if dd_val < -0.01:
|
|
805
|
-
drawdown_curve.append({
|
|
806
|
-
"date": drawdown.index[i].strftime("%Y-%m-%d"),
|
|
807
|
-
"equity": round(float(equity_series.iloc[i]), 2),
|
|
808
|
-
"drawdown_pct": dd_val,
|
|
809
|
-
})
|
|
810
|
-
|
|
811
|
-
# --- Trade log ---
|
|
812
|
-
trades_list = []
|
|
813
|
-
trades = stats.get("_trades")
|
|
814
|
-
if trades is not None and hasattr(trades, "iterrows"):
|
|
815
|
-
for i, trade in trades.iterrows():
|
|
816
|
-
size = trade.get("Size", 0)
|
|
817
|
-
entry_time = trade.get("EntryTime")
|
|
818
|
-
exit_time = trade.get("ExitTime")
|
|
819
|
-
duration = 0
|
|
820
|
-
if hasattr(entry_time, "strftime") and hasattr(exit_time, "strftime"):
|
|
821
|
-
duration = (exit_time - entry_time).days
|
|
822
|
-
trades_list.append({
|
|
823
|
-
"trade_number": i + 1,
|
|
824
|
-
"side": "LONG" if size > 0 else "SHORT",
|
|
825
|
-
"size": abs(int(size)) if size else 0,
|
|
826
|
-
"entry_bar": int(trade.get("EntryBar", 0)),
|
|
827
|
-
"exit_bar": int(trade.get("ExitBar", 0)),
|
|
828
|
-
"entry_date": entry_time.strftime("%Y-%m-%d") if hasattr(entry_time, "strftime") else str(entry_time),
|
|
829
|
-
"exit_date": exit_time.strftime("%Y-%m-%d") if hasattr(exit_time, "strftime") else str(exit_time),
|
|
830
|
-
"entry_price": _sf(trade.get("EntryPrice")),
|
|
831
|
-
"exit_price": _sf(trade.get("ExitPrice")),
|
|
832
|
-
"pnl_abs": _sf(trade.get("PnL")),
|
|
833
|
-
"pnl_pct": _sf(trade.get("ReturnPct")), # already in % form
|
|
834
|
-
"duration_days": duration,
|
|
835
|
-
})
|
|
836
|
-
|
|
837
|
-
# --- Monthly returns ---
|
|
838
|
-
monthly_returns = []
|
|
839
|
-
if ec is not None and hasattr(ec, "index"):
|
|
840
|
-
try:
|
|
841
|
-
equity_series = ec["Equity"]
|
|
842
|
-
monthly = equity_series.resample("ME").last()
|
|
843
|
-
pct = monthly.pct_change().dropna() * 100
|
|
844
|
-
for date, ret in pct.items():
|
|
845
|
-
monthly_returns.append({
|
|
846
|
-
"year": date.year,
|
|
847
|
-
"month": date.month,
|
|
848
|
-
"return_pct": _sf(ret),
|
|
849
|
-
})
|
|
850
|
-
except Exception:
|
|
851
|
-
pass
|
|
852
|
-
|
|
853
|
-
# --- Dates as YYYY-MM-DD ---
|
|
854
|
-
start_val = stats.get("Start")
|
|
855
|
-
end_val = stats.get("End")
|
|
856
|
-
start_date = start_val.strftime("%Y-%m-%d") if hasattr(start_val, "strftime") else str(start_val)
|
|
857
|
-
end_date = end_val.strftime("%Y-%m-%d") if hasattr(end_val, "strftime") else str(end_val)
|
|
858
|
-
|
|
859
|
-
return {
|
|
860
|
-
"name": name[:200],
|
|
861
|
-
"symbol": symbol[:50],
|
|
862
|
-
"description": description[:2000],
|
|
863
|
-
"tags": tags[:20],
|
|
864
|
-
"strategy_name": strategy_name,
|
|
865
|
-
"start_date": start_date,
|
|
866
|
-
"end_date": end_date,
|
|
867
|
-
"starting_capital": round(float(equity_curve[0]["equity"]), 2) if equity_curve else 0,
|
|
868
|
-
"parameters": parameters,
|
|
869
|
-
"summary": summary,
|
|
870
|
-
"equity_curve": equity_curve,
|
|
871
|
-
"drawdown_curve": drawdown_curve,
|
|
872
|
-
"trades": trades_list,
|
|
873
|
-
"monthly_returns": monthly_returns,
|
|
874
|
-
}
|
|
681
|
+
def save_optimization_result(
|
|
682
|
+
self,
|
|
683
|
+
stats,
|
|
684
|
+
heatmap,
|
|
685
|
+
name: str,
|
|
686
|
+
symbol: str = "",
|
|
687
|
+
description: str = "",
|
|
688
|
+
maximize="Sharpe Ratio",
|
|
689
|
+
param_ranges: dict = None,
|
|
690
|
+
) -> dict:
|
|
691
|
+
"""
|
|
692
|
+
Save optimization results to Rupeezy for viewing on the developer portal.
|
|
693
|
+
|
|
694
|
+
Supports multiple backtesting libraries (auto-detected from the result type):
|
|
695
|
+
- **backtesting.py**: pass stats + heatmap from bt.optimize(return_heatmap=True)
|
|
696
|
+
- **vectorbt**: pass Portfolio + metric Series from multi-param run
|
|
697
|
+
- **backtrader**: pass results list from cerebro.run() after optstrategy()
|
|
698
|
+
|
|
699
|
+
Args:
|
|
700
|
+
stats: The result object from any supported backtesting library.
|
|
701
|
+
heatmap: The heatmap/metric Series (backtesting.py/vectorbt) or
|
|
702
|
+
metric extraction callable (backtrader).
|
|
703
|
+
name (str): A label for this optimization run (e.g. "SMA Grid Search").
|
|
704
|
+
symbol (str, optional): Primary instrument symbol (e.g. "NIFTY").
|
|
705
|
+
description (str, optional): Notes about this optimization run.
|
|
706
|
+
maximize: The metric that was optimized. Should match the `maximize`
|
|
707
|
+
argument passed to Backtest.optimize(). Can be a string
|
|
708
|
+
metric name (e.g. "Sharpe Ratio") or a callable.
|
|
709
|
+
Defaults to "Sharpe Ratio".
|
|
710
|
+
param_ranges (dict, optional): Explicit parameter range definitions.
|
|
711
|
+
Keys are parameter names, values are range() objects or lists.
|
|
712
|
+
Example: {"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)}
|
|
713
|
+
If not provided, ranges are inferred from the heatmap index.
|
|
714
|
+
|
|
715
|
+
Returns:
|
|
716
|
+
dict: {"status": "success", "optimization_id": "opt_xxx", "backtest_id": "bt_xxx"}
|
|
717
|
+
|
|
718
|
+
Example::
|
|
719
|
+
|
|
720
|
+
stats, heatmap = bt.optimize(
|
|
721
|
+
sma_fast=range(5, 51, 5),
|
|
722
|
+
sma_slow=range(20, 201, 10),
|
|
723
|
+
maximize='Sharpe Ratio',
|
|
724
|
+
return_heatmap=True,
|
|
725
|
+
)
|
|
726
|
+
client.save_optimization_result(
|
|
727
|
+
stats=stats,
|
|
728
|
+
heatmap=heatmap,
|
|
729
|
+
name="SMA Crossover Grid Search",
|
|
730
|
+
symbol="NIFTY",
|
|
731
|
+
maximize='Sharpe Ratio',
|
|
732
|
+
param_ranges={"sma_fast": range(5, 51, 5), "sma_slow": range(20, 201, 10)},
|
|
733
|
+
)
|
|
734
|
+
"""
|
|
735
|
+
is_maximize = True
|
|
736
|
+
objective_metric = maximize
|
|
737
|
+
|
|
738
|
+
if isinstance(maximize, bool):
|
|
739
|
+
is_maximize = maximize
|
|
740
|
+
objective_metric = "Sharpe Ratio"
|
|
741
|
+
|
|
742
|
+
from .backtest import serialize_optimization
|
|
743
|
+
payload = serialize_optimization(
|
|
744
|
+
result=stats,
|
|
745
|
+
heatmap=heatmap,
|
|
746
|
+
name=name,
|
|
747
|
+
symbol=symbol,
|
|
748
|
+
description=description,
|
|
749
|
+
objective_metric=objective_metric,
|
|
750
|
+
maximize=is_maximize,
|
|
751
|
+
param_ranges=param_ranges,
|
|
752
|
+
)
|
|
753
|
+
endpoint = "/strategies/optimizations"
|
|
754
|
+
return self._make_api_request("POST", endpoint, data=payload)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Backtest serialization subpackage.
|
|
2
|
+
|
|
3
|
+
Auto-detects the backtesting library from the result object type and
|
|
4
|
+
dispatches to the appropriate adapter. Supports:
|
|
5
|
+
|
|
6
|
+
- backtesting.py (Backtest.run() / Backtest.optimize())
|
|
7
|
+
- vectorbt (vbt.Portfolio)
|
|
8
|
+
- backtrader (cerebro.run() Strategy)
|
|
9
|
+
|
|
10
|
+
Usage from api.py:
|
|
11
|
+
from .backtest import serialize_stats, serialize_optimization
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _is_backtestingpy(result):
|
|
16
|
+
"""Check if result is a backtesting.py stats object (pd.Series with _strategy key)."""
|
|
17
|
+
return hasattr(result, "get") and result.get("_strategy") is not None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _is_vectorbt(result):
|
|
21
|
+
"""Check if result is a vectorbt Portfolio object."""
|
|
22
|
+
return (hasattr(result, "stats")
|
|
23
|
+
and hasattr(result, "value")
|
|
24
|
+
and hasattr(result, "trades"))
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _is_backtrader(result):
|
|
28
|
+
"""Check if result is a backtrader Strategy object."""
|
|
29
|
+
# Single strategy
|
|
30
|
+
if hasattr(result, "analyzers"):
|
|
31
|
+
return True
|
|
32
|
+
# List of strategies from cerebro.run()
|
|
33
|
+
if isinstance(result, list) and len(result) > 0:
|
|
34
|
+
item = result[0]
|
|
35
|
+
if hasattr(item, "analyzers"):
|
|
36
|
+
return True
|
|
37
|
+
# Optimization result: list of lists
|
|
38
|
+
if isinstance(item, list) and len(item) > 0 and hasattr(item[0], "analyzers"):
|
|
39
|
+
return True
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def serialize_stats(result, name, symbol, description, tags):
|
|
44
|
+
"""Auto-detect library and serialize backtest stats to backend payload.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
result: Stats/Portfolio/Strategy object from any supported library.
|
|
48
|
+
name: Backtest name.
|
|
49
|
+
symbol: Trading symbol.
|
|
50
|
+
description: Description text.
|
|
51
|
+
tags: List of tag strings.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
dict: Payload ready for POST to /strategies/backtests.
|
|
55
|
+
|
|
56
|
+
Raises:
|
|
57
|
+
TypeError: If the result type is not recognized.
|
|
58
|
+
"""
|
|
59
|
+
if _is_backtestingpy(result):
|
|
60
|
+
from ._backtestingpy import serialize_stats as _serialize
|
|
61
|
+
return _serialize(result, name, symbol, description, tags)
|
|
62
|
+
|
|
63
|
+
if _is_vectorbt(result):
|
|
64
|
+
from ._vectorbt import serialize_stats as _serialize
|
|
65
|
+
return _serialize(result, name, symbol, description, tags)
|
|
66
|
+
|
|
67
|
+
if _is_backtrader(result):
|
|
68
|
+
from ._backtrader import serialize_stats as _serialize
|
|
69
|
+
return _serialize(result, name, symbol, description, tags)
|
|
70
|
+
|
|
71
|
+
raise TypeError(
|
|
72
|
+
f"Unsupported backtest result type: {type(result).__name__}. "
|
|
73
|
+
f"Expected stats from backtesting.py, a vectorbt Portfolio, "
|
|
74
|
+
f"or a backtrader Strategy."
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def serialize_optimization(result, heatmap, name, symbol, description,
|
|
79
|
+
objective_metric, maximize, param_ranges):
|
|
80
|
+
"""Auto-detect library and serialize optimization results.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
result: Stats/Portfolio/Strategy-list from any supported library.
|
|
84
|
+
heatmap: Heatmap Series (backtesting.py/vectorbt) or metric_fn (backtrader).
|
|
85
|
+
name: Optimization name.
|
|
86
|
+
symbol: Trading symbol.
|
|
87
|
+
description: Description text.
|
|
88
|
+
objective_metric: Metric that was optimized.
|
|
89
|
+
maximize: Boolean — True to maximize, False to minimize.
|
|
90
|
+
param_ranges: Dict of param_name -> range/list.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
dict: Payload ready for POST to /strategies/optimizations.
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
TypeError: If the result type is not recognized.
|
|
97
|
+
"""
|
|
98
|
+
if _is_backtestingpy(result):
|
|
99
|
+
from ._backtestingpy import serialize_optimization as _serialize
|
|
100
|
+
return _serialize(result, heatmap, name, symbol, description,
|
|
101
|
+
objective_metric, maximize, param_ranges)
|
|
102
|
+
|
|
103
|
+
if _is_vectorbt(result):
|
|
104
|
+
from ._vectorbt import serialize_optimization as _serialize
|
|
105
|
+
return _serialize(result, heatmap, name, symbol, description,
|
|
106
|
+
objective_metric, maximize, param_ranges)
|
|
107
|
+
|
|
108
|
+
if _is_backtrader(result):
|
|
109
|
+
from ._backtrader import serialize_optimization as _serialize
|
|
110
|
+
return _serialize(result, heatmap, name, symbol, description,
|
|
111
|
+
objective_metric, maximize, param_ranges)
|
|
112
|
+
|
|
113
|
+
raise TypeError(
|
|
114
|
+
f"Unsupported optimization result type: {type(result).__name__}. "
|
|
115
|
+
f"Expected stats from backtesting.py, a vectorbt Portfolio, "
|
|
116
|
+
f"or backtrader optimization results."
|
|
117
|
+
)
|