ivolatility-backtesting 1.34__tar.gz → 1.35__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ivolatility_backtesting
3
- Version: 1.34
3
+ Version: 1.35
4
4
  Summary: A universal backtesting framework for financial strategies using the IVolatility API.
5
5
  Author-email: IVolatility <support@ivolatility.com>
6
6
  Project-URL: Homepage, https://ivolatility.com
@@ -3656,10 +3656,10 @@ def _api_call_internal(endpoint, cache_config, debug, debug_level, **kwargs):
3656
3656
  elif debug or cache_config.get('debug', False):
3657
3657
  print(f"[CACHE] ✓ Cache hit: {endpoint} ({len(cached_data) if hasattr(cached_data, '__len__') else '?'} records)")
3658
3658
 
3659
- # ✅ OPTIMIZED: Return DataFrame directly without conversion!
3660
- # Flag '_is_dataframe' tells caller to skip pd.DataFrame() creation
3659
+ # ✅ CONSISTENT: Always return list format for backward compatibility
3660
+ # This ensures `if not response.get('data')` works correctly in all strategies
3661
3661
  if isinstance(cached_data, pd.DataFrame):
3662
- return {'data': cached_data, 'status': 'success', '_is_dataframe': True}
3662
+ return {'data': cached_data.to_dict('records'), 'status': 'success'}
3663
3663
  return cached_data
3664
3664
 
3665
3665
  if debug_level >= 3:
@@ -3773,6 +3773,84 @@ def _api_call_internal(endpoint, cache_config, debug, debug_level, **kwargs):
3773
3773
  return None
3774
3774
 
3775
3775
 
3776
+ # ============================================================
3777
+ # API RESPONSE HELPERS
3778
+ # ============================================================
3779
+ def get_api_data(response, as_dataframe=True):
3780
+ """
3781
+ Universal helper to extract data from api_call response.
3782
+ Works with both list and DataFrame formats from cache.
3783
+
3784
+ Args:
3785
+ response: Response dict from api_call()
3786
+ as_dataframe: If True (default), always return DataFrame.
3787
+ If False, return list of dicts.
3788
+
3789
+ Returns:
3790
+ DataFrame/list or None if no valid data
3791
+
3792
+ Example:
3793
+ # Get stock data as DataFrame
3794
+ stock_df = get_api_data(api_call('/equities/eod/stock-prices', ...))
3795
+ if stock_df is None:
3796
+ print("No data!")
3797
+
3798
+ # Get as list of dicts
3799
+ records = get_api_data(response, as_dataframe=False)
3800
+ """
3801
+ if response is None:
3802
+ return None
3803
+
3804
+ data = response.get('data')
3805
+ if data is None:
3806
+ return None
3807
+
3808
+ # Handle DataFrame (from cache)
3809
+ if isinstance(data, pd.DataFrame):
3810
+ if data.empty:
3811
+ return None
3812
+ return data if as_dataframe else data.to_dict('records')
3813
+
3814
+ # Handle list (from API)
3815
+ if not data: # empty list
3816
+ return None
3817
+
3818
+ return pd.DataFrame(data) if as_dataframe else data
3819
+
3820
+
3821
+ def is_api_response_valid(response):
3822
+ """
3823
+ Check if api_call response contains valid non-empty data.
3824
+ Works with both list and DataFrame formats.
3825
+
3826
+ Args:
3827
+ response: Response dict from api_call()
3828
+
3829
+ Returns:
3830
+ bool: True if response contains valid non-empty data
3831
+
3832
+ Example:
3833
+ response = api_call('/equities/eod/stock-prices', ...)
3834
+ if not is_api_response_valid(response):
3835
+ print("No data available!")
3836
+ return
3837
+
3838
+ # Safe to use response['data'] now
3839
+ df = pd.DataFrame(response['data'])
3840
+ """
3841
+ if response is None:
3842
+ return False
3843
+
3844
+ data = response.get('data')
3845
+ if data is None:
3846
+ return False
3847
+
3848
+ if isinstance(data, pd.DataFrame):
3849
+ return not data.empty
3850
+
3851
+ return bool(data)
3852
+
3853
+
3776
3854
  # ============================================================
3777
3855
  # OPTIONS DATA HELPERS
3778
3856
  # ============================================================
@@ -8918,11 +8996,7 @@ def preload_data_universal(config, data_requests=None, debug=False):
8918
8996
 
8919
8997
  response = api_call(endpoint, cache_config, debug=debuginfo, **params)
8920
8998
  if response and 'data' in response:
8921
- # OPTIMIZED: Skip DataFrame creation if already DataFrame (from memory cache)
8922
- if response.get('_is_dataframe'):
8923
- df = response['data']
8924
- else:
8925
- df = pd.DataFrame(response['data'])
8999
+ df = pd.DataFrame(response['data'])
8926
9000
  if len(df) > 0:
8927
9001
  all_data.append(df)
8928
9002
 
@@ -8983,11 +9057,7 @@ def preload_data_universal(config, data_requests=None, debug=False):
8983
9057
 
8984
9058
  response = api_call(endpoint, cache_config, debug=debuginfo, **params)
8985
9059
  if response and 'data' in response:
8986
- # OPTIMIZED: Skip DataFrame creation if already DataFrame (from memory cache)
8987
- if response.get('_is_dataframe'):
8988
- df = response['data'] # Already a DataFrame!
8989
- else:
8990
- df = pd.DataFrame(response['data'])
9060
+ df = pd.DataFrame(response['data'])
8991
9061
  if len(df) > 0:
8992
9062
  # Add cp type if not in response
8993
9063
  if cp_type and 'type' not in df.columns and 'optionType' not in df.columns:
@@ -9009,11 +9079,7 @@ def preload_data_universal(config, data_requests=None, debug=False):
9009
9079
  params = base_params.copy()
9010
9080
  response = api_call(endpoint, cache_config, debug=debuginfo, **params)
9011
9081
  if response and 'data' in response:
9012
- # OPTIMIZED: Skip DataFrame creation if already DataFrame (from memory cache)
9013
- if response.get('_is_dataframe'):
9014
- df = response['data']
9015
- else:
9016
- df = pd.DataFrame(response['data'])
9082
+ df = pd.DataFrame(response['data'])
9017
9083
  if len(df) > 0:
9018
9084
  all_data.append(df)
9019
9085
 
@@ -11217,7 +11283,7 @@ class UniversalCacheManager:
11217
11283
  __all__ = [
11218
11284
  'BacktestResults', 'BacktestAnalyzer', 'ResultsReporter',
11219
11285
  'ChartGenerator', 'ResultsExporter', 'run_backtest', 'run_backtest_with_stoploss',
11220
- 'init_api', 'api_call', 'APIHelper', 'APIManager',
11286
+ 'init_api', 'api_call', 'get_api_data', 'is_api_response_valid', 'APIHelper', 'APIManager',
11221
11287
  'ResourceMonitor', 'create_progress_bar', 'update_progress', 'format_time',
11222
11288
  'StopLossManager', 'PositionManager', 'StopLossConfig',
11223
11289
  'calculate_stoploss_metrics', 'print_stoploss_section', 'create_stoploss_charts',
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ivolatility_backtesting
3
- Version: 1.34
3
+ Version: 1.35
4
4
  Summary: A universal backtesting framework for financial strategies using the IVolatility API.
5
5
  Author-email: IVolatility <support@ivolatility.com>
6
6
  Project-URL: Homepage, https://ivolatility.com
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ivolatility_backtesting"
7
- version = "1.34"
7
+ version = "1.35"
8
8
  description = "A universal backtesting framework for financial strategies using the IVolatility API."
9
9
  readme = "README.md"
10
10
  authors = [