pwb-toolbox 0.1.3__tar.gz → 0.1.5__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.3
3
+ Version: 0.1.5
4
4
  Summary: A toolbox library for quant traders
5
5
  Home-page: https://github.com/paperswithbacktest/pwb-toolbox
6
6
  Author: Your Name
@@ -627,7 +627,9 @@ def load_dataset(
627
627
  return df
628
628
 
629
629
 
630
- def __convert_indices_to_usd(df_indices, df_forex):
630
+ def __convert_indices_to_usd(
631
+ df_indices: pd.DataFrame, df_forex: pd.DataFrame
632
+ ) -> pd.DataFrame:
631
633
  mapping = {
632
634
  "ADSMI": "AED", # United Arab Emirates
633
635
  "AEX": "EUR", # Netherlands
@@ -727,32 +729,40 @@ def __convert_indices_to_usd(df_indices, df_forex):
727
729
  "SX5E": "EUR", # Europe
728
730
  "TA125": "ILS", # Israel
729
731
  }
730
- symbols = df_indices.symbol.unique()
731
- mapping = {k: v for k, v in mapping.items() if k in symbols}
732
732
  frames = []
733
- for symbol, currency in mapping.items():
734
- df_index = df_indices[df_indices["symbol"] == symbol].copy()
735
- if currency == "USD":
736
- frames.append(df_index)
733
+
734
+ # iterate over the symbols that actually exist in df_indices
735
+ for symbol in df_indices["symbol"].unique():
736
+ df_idx = df_indices[df_indices["symbol"] == symbol].copy()
737
+
738
+ # 1) Figure out what currency the index is quoted in.
739
+ ccy = mapping.get(symbol) # None if not mapped
740
+ if ccy is None or ccy == "USD":
741
+ # Unknown or already USD – just keep the original rows
742
+ frames.append(df_idx)
737
743
  continue
738
- df_forex_currency = df_forex[df_forex["symbol"] == currency + "USD"].copy()
739
- if df_index.empty or df_forex_currency.empty:
744
+
745
+ # 2) Find the matching FX rate (home-ccy → USD)
746
+ pair = ccy + "USD"
747
+ df_fx = df_forex[df_forex["symbol"] == pair].copy()
748
+
749
+ if df_idx.empty or df_fx.empty:
750
+ # No FX data – keep raw index levels instead of dropping them
751
+ frames.append(df_idx)
740
752
  continue
741
- # Merge dataframes on the date column
742
- merged_df = pd.merge(
743
- df_index, df_forex_currency, on="date", suffixes=("", "_forex")
744
- )
745
753
 
746
- # Multiply the index prices by the corresponding forex rates
747
- merged_df["open"] = merged_df["open"] * merged_df["open_forex"]
748
- merged_df["high"] = merged_df["high"] * merged_df["high_forex"]
749
- merged_df["low"] = merged_df["low"] * merged_df["low_forex"]
750
- merged_df["close"] = merged_df["close"] * merged_df["close_forex"]
754
+ # 3) Merge on date and convert OHLC
755
+ merged = pd.merge(df_idx, df_fx, on="date", suffixes=("", "_fx"))
756
+ for col in ("open", "high", "low", "close"):
757
+ merged[col] = merged[col] * merged[f"{col}_fx"]
751
758
 
752
- frames.append(merged_df[["symbol", "date", "open", "high", "low", "close"]])
759
+ frames.append(merged[["symbol", "date", "open", "high", "low", "close"]])
753
760
 
754
- df = pd.concat(frames, ignore_index=True)
755
- return df
761
+ if not frames:
762
+ return pd.DataFrame(columns=df_indices.columns)
763
+
764
+ # Combine everything back into one DataFrame
765
+ return pd.concat(frames, ignore_index=True)
756
766
 
757
767
 
758
768
  def __extract_years_to_maturity(bond_symbol):
@@ -861,37 +871,90 @@ def __extend_etfs(df_etfs):
861
871
  return df
862
872
 
863
873
 
874
+ ALLOWED_FIELDS = {"open", "high", "low", "close"}
875
+
876
+
864
877
  def get_pricing(
865
878
  symbol_list,
866
- fields=["close"],
879
+ fields=None, # ← default set below
867
880
  start_date="1980-01-01",
868
881
  end_date=date.today().isoformat(),
869
882
  extend=False,
883
+ keep_single_level=True, # backward-compat flag
870
884
  ):
871
885
  """
872
- Get pricing data for a list of symbols
886
+ Fetch OHLC pricing for the requested symbols.
887
+
888
+ Parameters
889
+ ----------
890
+ symbol_list : str | list[str]
891
+ One ticker or a list of tickers.
892
+ fields : list[str] | None
893
+ Any subset of ["open", "high", "low", "close"].
894
+ Defaults to ["close"] for backward compatibility.
895
+ start_date, end_date : str (YYYY-MM-DD)
896
+ Slice the date index (inclusive).
897
+ extend : bool
898
+ Pass-through to `load_dataset(..., extend=extend)`.
899
+ keep_single_level : bool
900
+ If `True` and only one field is requested, flatten the columns so the
901
+ output matches the old behaviour (columns = symbols). If `False`
902
+ you always get a two-level MultiIndex `(symbol, field)`.
903
+
904
+ Returns
905
+ -------
906
+ pd.DataFrame
907
+ * MultiIndex columns (symbol, field) when `len(fields) > 1`
908
+ * Single-level columns (symbol) when one field & keep_single_level=True
873
909
  """
910
+ # ------------------------------------------------------------------ sanity
911
+ if fields is None:
912
+ fields = ["close"]
874
913
  if isinstance(symbol_list, str):
875
914
  symbol_list = [symbol_list]
876
- if len(fields) != 1:
877
- raise ValueError("Only one field is allowed")
878
- if fields[0] not in ["open", "high", "low", "close"]:
879
- raise ValueError("Invalid field")
880
- df = pd.concat(
881
- [
882
- load_dataset("Stocks-Daily-Price", symbol_list, extend=extend),
883
- load_dataset("ETFs-Daily-Price", symbol_list, extend=extend),
884
- load_dataset("Cryptocurrencies-Daily-Price", symbol_list, extend=extend),
885
- load_dataset("Bonds-Daily-Price", symbol_list, extend=extend),
886
- load_dataset("Commodities-Daily-Price", symbol_list, extend=extend),
887
- ]
888
- )
915
+
916
+ fields = [f.lower() for f in fields]
917
+ bad = [f for f in fields if f not in ALLOWED_FIELDS]
918
+ if bad:
919
+ raise ValueError(f"Invalid field(s): {bad}. Allowed: {sorted(ALLOWED_FIELDS)}")
920
+
921
+ # --------------------------------------------------------------- download
922
+ DATASETS = [
923
+ ("Stocks-Daily-Price", extend),
924
+ ("ETFs-Daily-Price", extend),
925
+ ("Cryptocurrencies-Daily-Price", extend),
926
+ ("Bonds-Daily-Price", extend),
927
+ ("Commodities-Daily-Price", extend),
928
+ ("Indices-Daily-Price", False), # indices generally have no proxy data
929
+ ]
930
+ remaining = set(symbol_list) # symbols still to fetch
931
+ frames = []
932
+ for dataset_name, ext_flag in DATASETS:
933
+ if not remaining: # all symbols resolved → stop early
934
+ break
935
+ df_part = load_dataset(dataset_name, list(remaining), extend=ext_flag)
936
+ if not df_part.empty:
937
+ frames.append(df_part)
938
+ remaining -= set(df_part["symbol"].unique())
939
+ df = pd.concat(frames, ignore_index=True)
940
+
889
941
  df["date"] = pd.to_datetime(df["date"])
890
942
  df.set_index("date", inplace=True)
891
943
  df.sort_index(inplace=True)
892
- df_filtered = df.loc[start_date:end_date]
893
- prices_df = df_filtered.pivot_table(
894
- values="close", index=df_filtered.index, columns="symbol"
895
- )
896
- prices_df.columns = list(prices_df.columns)
897
- return prices_df
944
+ df = df.loc[start_date:end_date]
945
+
946
+ # ------------------------------------------------------------- reshape
947
+ # Pivot can accept a list of values: returns columns = (field, symbol)
948
+ prices = df.pivot_table(values=fields, index=df.index, columns="symbol")
949
+
950
+ # Make outer level = symbol, inner = field → pivot_df[sym] gives OHLC block
951
+ prices = prices.swaplevel(axis=1).sort_index(axis=1)
952
+
953
+ # Optional: flatten back to the legacy layout if only one field requested
954
+ if keep_single_level and len(fields) == 1:
955
+ field = fields[0]
956
+ prices.columns = prices.columns.droplevel(1) # keep only the symbol names
957
+ # optional: rename index level for clarity
958
+ prices.name = field
959
+
960
+ return prices
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pwb-toolbox
3
- Version: 0.1.3
3
+ Version: 0.1.5
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.3
3
+ version = 0.1.5
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