diff --git a/coin2086/pnl.py b/coin2086/pnl.py index 26ea129..981cb09 100644 --- a/coin2086/pnl.py +++ b/coin2086/pnl.py @@ -8,7 +8,7 @@ def add_portfolio_purchase_price(trades, initial_purchase_price): - trades["portfolio_purchase_price"] = 0 + trades["portfolio_purchase_price"] = 0.0 trades.loc[trades["trade_side"] == "BUY", "portfolio_purchase_price"] = ( trades["amount"] + trades["fee"] ) diff --git a/coin2086/pricedownload.py b/coin2086/pricedownload.py index 2fcda56..efd69f8 100644 --- a/coin2086/pricedownload.py +++ b/coin2086/pricedownload.py @@ -18,7 +18,8 @@ class PriceDownloader(abc.ABC): def download_price(self, crypto, dtime): pass - @abc.abstractproperty + @property + @abc.abstractmethod def supported_crypto_list(self): pass diff --git a/coin2086/valuation.py b/coin2086/valuation.py index 9771394..2a6476c 100644 --- a/coin2086/valuation.py +++ b/coin2086/valuation.py @@ -96,6 +96,8 @@ def unstack_portfolio_composition(trades, sales, initial_portfolio): portfolio = portfolio.drop(columns=["sign", "trade_side"]) # Unstack the composition of the portofolio after each transaction portfolio = portfolio.set_index("cryptocurrency", append=True).unstack().fillna(0) + # Ensure column names are preserved for pandas 2.x compatibility + portfolio.columns.names = [None, 'cryptocurrency'] # We need the valuation of the portoflio *before* each transaction # so shift by one portfolio["quantity"] = portfolio["quantity"].cumsum().shift(1, fill_value=0) @@ -105,10 +107,13 @@ def unstack_portfolio_composition(trades, sales, initial_portfolio): def merge_rates_and_valuate(portfolio): - portfolio = portfolio.stack() + # Stack the second level (cryptocurrency) into the index + portfolio = portfolio.stack(level=1, future_stack=True) portfolio["ref_price"] = portfolio["sell_price"].fillna(portfolio["public_price"]) portfolio["value"] = portfolio["ref_price"] * portfolio["quantity"] - portfolio = portfolio.unstack() + # Unstack back and explicitly set the column names + portfolio = portfolio.unstack(level=-1) + portfolio.columns.names = [None, 'cryptocurrency'] portfolio["value", "TOTAL"] = portfolio["value"].sum(axis=1) return portfolio @@ -117,6 +122,8 @@ def add_sell_prices(portfolio, sales): sell_prices = sales[["cryptocurrency", "price"]] sell_prices = sell_prices.rename(columns={"price": "sell_price"}) sell_prices = sell_prices.set_index("cryptocurrency", append=True).unstack() + # Ensure column names are preserved for pandas 2.x compatibility + sell_prices.columns.names = [None, 'cryptocurrency'] return portfolio.join(sell_prices, how="outer") diff --git a/pyproject.toml b/pyproject.toml index ea88eaf..3c136a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.6.2,<4.0" -pandas = "^1.1" +pandas = ">=1.1,<3.0" requests = "^2.10" [tool.poetry.dev-dependencies] diff --git a/tests/reference_data/interleaved_multiyear_trades_detailed_pnl.csv b/tests/reference_data/interleaved_multiyear_trades_detailed_pnl.csv index 81bd580..28d17a8 100644 --- a/tests/reference_data/interleaved_multiyear_trades_detailed_pnl.csv +++ b/tests/reference_data/interleaved_multiyear_trades_detailed_pnl.csv @@ -1,6 +1,6 @@ ,datetime,trade_side,cryptocurrency,quantity,amount,fee,amount_net,portfolio_value,portfolio_purchase_price,purchase_price_fraction,purchase_price_fraction_sum,portfolio_purchase_price_net,pnl 1,2019-11-14 19:50:00,SELL,BTC,0.5,3922.44,19.6122,3902.8278,7844.88,7185.1269,3592.56345,0.0,7185.1269,310.2643499999999 4,2020-09-05 16:50:00,SELL,BTC,1.0,8722.7,43.6135,8679.086500000001,23310.95,27780.330599999998,9050.795292311339,3592.56345,24187.767149999996,-371.7087923113377 -5,2020-09-08 12:40:00,SELL,ETH,5.0,1425.35,7.12675,1418.22325,14189.210000000001,27780.330599999998,1520.5556079130922,12643.358742311339,15136.971857688659,-102.33235791309221 +5,2020-09-08 12:40:00,SELL,ETH,5.0,1425.35,7.1267499999999995,1418.22325,14189.210000000001,27780.330599999998,1520.5556079130922,12643.358742311339,15136.971857688659,-102.33235791309221 6,2020-12-20 09:10:00,SELL,BTC,0.25,4805.975,24.029875000000004,4781.945125,28835.850000000002,27780.330599999998,2269.4027082959274,14163.914350224431,13616.416249775566,2512.542416704073 7,2021-03-13 23:40:00,SELL,BTC,0.25,12506.2925,62.5314625,12443.7610375,62531.462499999994,27780.330599999998,2269.402708295928,16433.31705852036,11347.013541479639,10174.358329204073 diff --git a/tests/reference_data/interleaved_trades_detailed_pnl.csv b/tests/reference_data/interleaved_trades_detailed_pnl.csv index b48ed69..c0dc9b4 100644 --- a/tests/reference_data/interleaved_trades_detailed_pnl.csv +++ b/tests/reference_data/interleaved_trades_detailed_pnl.csv @@ -1,4 +1,4 @@ ,datetime,trade_side,cryptocurrency,quantity,amount,fee,amount_net,portfolio_value,portfolio_purchase_price,purchase_price_fraction,purchase_price_fraction_sum,portfolio_purchase_price_net,pnl 2,2020-09-05 16:50:00,SELL,BTC,0.5,4361.35,21.80675,4339.543250000001,10226.900000000001,11286.471599999999,4813.213477462378,0.0,11286.471599999999,-473.6702274623776 -3,2020-09-08 12:40:00,SELL,ETH,5.0,1425.35,7.12675,1418.22325,5679.969999999999,11286.471599999999,1624.4202812618723,4813.213477462378,6473.258122537621,-206.19703126187233 +3,2020-09-08 12:40:00,SELL,ETH,5.0,1425.35,7.1267499999999995,1418.22325,5679.969999999999,11286.471599999999,1624.4202812618723,4813.213477462378,6473.258122537621,-206.19703126187233 6,2020-12-21 09:30:00,SELL,BTC,1.0,19531.69,97.65845,19434.03155,31934.784999999996,22507.658399999997,9828.616024368384,6437.633758724251,16070.024641275746,9605.415525631615 diff --git a/tests/test_non_regression.py b/tests/test_non_regression.py index bd1d018..5bebbcb 100644 --- a/tests/test_non_regression.py +++ b/tests/test_non_regression.py @@ -69,7 +69,9 @@ def test_compute_pnl(): ) pnl_declare_ref = pd.read_csv(pnl_declare_path, index_col=0) # Convert 2nd columns (Date de la cession to datetime64s) - pnl_declare_ref.iloc[:, 1] = pd.to_datetime(pnl_declare_ref.iloc[:, 1]) + # Use column name instead of iloc for pandas 2.x compatibility + date_col_name = pnl_declare_ref.columns[1] + pnl_declare_ref[date_col_name] = pd.to_datetime(pnl_declare_ref[date_col_name]) pnl_declare, total_pnl = coin2086.compute_taxable_pnls(trades, 2020) print(pnl_declare) pd.testing.assert_frame_equal(pnl_declare, pnl_declare_ref)