pwb-toolbox 0.1.5__tar.gz → 0.1.6__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: pwb-toolbox
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: A toolbox library for quant traders
5
5
  Home-page: https://github.com/paperswithbacktest/pwb-toolbox
6
6
  Author: Your Name
@@ -813,62 +813,68 @@ def __extend_etfs(df_etfs):
813
813
  symbols = df_etfs.symbol.unique()
814
814
  mapping = {k: v for k, v in mapping.items() if k in symbols}
815
815
 
816
- grouped_path_symbols = defaultdict(list)
817
- for value in mapping.values():
818
- grouped_path_symbols[value[0]].append(value[1])
819
- grouped_path_symbols = dict(grouped_path_symbols)
820
- df_others = pd.concat(
821
- [
822
- load_dataset(path, symbols, to_usd=True)
823
- for path, symbols in grouped_path_symbols.items()
824
- ]
825
- )
816
+ # Nothing to extend → just return the input
817
+ if not mapping:
818
+ return df_etfs.copy()
819
+
820
+ # ------------------------------------------------------------------ step 2
821
+ grouped = defaultdict(list) # {path: [proxy1, proxy2, ...]}
822
+ for _, (path, proxy) in mapping.items():
823
+ grouped[path].append(proxy)
824
+
825
+ # Load each dataset only if there's at least one proxy symbol
826
+ other_frames = []
827
+ for path, proxies in grouped.items():
828
+ if proxies: # skip empty lists
829
+ other_frames.append(load_dataset(path, proxies, to_usd=True))
826
830
 
831
+ # If no proxy data could be loaded, fall back to raw ETF data
832
+ if not other_frames:
833
+ return df_etfs.copy()
834
+
835
+ df_others = pd.concat(other_frames, ignore_index=True)
836
+
837
+ # ------------------------------------------------------------------ step 3
827
838
  frames = []
828
- for etf, other in mapping.items():
829
- other_symbol = other[1]
830
- # Get the ETF & Index data
839
+ for etf, (__, proxy) in mapping.items():
831
840
  etf_data = df_etfs[df_etfs["symbol"] == etf]
832
- if etf_data.empty:
833
- continue
834
- other_data = df_others[df_others["symbol"] == other_symbol]
835
- if other_data.empty:
836
- continue
837
-
838
- # Find the first overlapping date
839
- common_dates = etf_data["date"].isin(other_data["date"])
840
- first_common_date = etf_data.loc[common_dates, "date"].min()
841
+ proxy_data = df_others[df_others["symbol"] == proxy]
841
842
 
842
- if pd.isnull(first_common_date):
843
- print(f"No common date found for {etf} and {other_symbol}")
843
+ if etf_data.empty or proxy_data.empty:
844
+ frames.append(etf_data) # keep raw ETF if proxy missing
844
845
  continue
845
846
 
846
- etf_first_common = etf_data[etf_data["date"] == first_common_date]
847
- other_first_common = other_data[other_data["date"] == first_common_date]
847
+ # Find first overlapping date
848
+ first_common = etf_data.loc[
849
+ etf_data["date"].isin(proxy_data["date"]), "date"
850
+ ].min()
851
+ if pd.isna(first_common):
852
+ frames.append(etf_data) # no overlap → keep raw ETF
853
+ continue
848
854
 
849
- # Compute the adjustment factor (using closing prices for simplicity)
850
- adjustment_factor = (
851
- etf_first_common["close"].values[0] / other_first_common["close"].values[0]
855
+ # Compute adjustment factor on that date
856
+ k = (
857
+ etf_data.loc[etf_data["date"] == first_common, "close"].iloc[0]
858
+ / proxy_data.loc[proxy_data["date"] == first_common, "close"].iloc[0]
852
859
  )
853
860
 
854
- # Adjust index data before the first common date
855
- index_data_before_common = other_data[
856
- other_data["date"] < first_common_date
857
- ].copy()
858
- for column in ["open", "high", "low", "close"]:
859
- index_data_before_common.loc[:, column] *= adjustment_factor
860
- index_data_before_common.loc[:, "symbol"] = etf
861
+ # Scale proxy history before the overlap
862
+ hist = proxy_data[proxy_data["date"] < first_common].copy()
863
+ hist[["open", "high", "low", "close"]] *= k
864
+ hist["symbol"] = etf
861
865
 
862
- # Combine adjusted index data with ETF data
863
- combined_data = pd.concat([index_data_before_common, etf_data])
864
- frames.append(combined_data)
866
+ # Combine proxy history + actual ETF data
867
+ frames.append(pd.concat([hist, etf_data]))
865
868
 
866
- symbols_not_in_mapping = set(symbols) - set(mapping.keys())
867
- frames.append(df_etfs[df_etfs["symbol"].isin(symbols_not_in_mapping)])
869
+ # Add ETFs that were never in the mapping
870
+ untouched = set(symbols) - set(mapping)
871
+ frames.append(df_etfs[df_etfs["symbol"].isin(untouched)])
868
872
 
869
- # Concatenate all frames to form the final dataframe
870
- df = pd.concat(frames).sort_values(by=["date", "symbol"]).reset_index(drop=True)
871
- return df
873
+ return (
874
+ pd.concat(frames, ignore_index=True)
875
+ .sort_values(["date", "symbol"])
876
+ .reset_index(drop=True)
877
+ )
872
878
 
873
879
 
874
880
  ALLOWED_FIELDS = {"open", "high", "low", "close"}
@@ -925,6 +931,7 @@ def get_pricing(
925
931
  ("Cryptocurrencies-Daily-Price", extend),
926
932
  ("Bonds-Daily-Price", extend),
927
933
  ("Commodities-Daily-Price", extend),
934
+ ("Forex-Daily-Price", extend),
928
935
  ("Indices-Daily-Price", False), # indices generally have no proxy data
929
936
  ]
930
937
  remaining = set(symbol_list) # symbols still to fetch
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pwb-toolbox
3
- Version: 0.1.5
3
+ Version: 0.1.6
4
4
  Summary: A toolbox library for quant traders
5
5
  Home-page: https://github.com/paperswithbacktest/pwb-toolbox
6
6
  Author: Your Name
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = pwb-toolbox
3
- version = 0.1.5
3
+ version = 0.1.6
4
4
  author = Your Name
5
5
  author_email = hello@paperswithbacktest.com
6
6
  description = A toolbox library for quant traders
File without changes
File without changes
File without changes