prosperity3bt 0.1.0__tar.gz → 0.2.0__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.
Files changed (24) hide show
  1. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/PKG-INFO +4 -2
  2. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/README.md +3 -1
  3. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/__main__.py +5 -35
  4. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/data.py +6 -6
  5. prosperity3bt-0.2.0/prosperity3bt/open.py +36 -0
  6. prosperity3bt-0.2.0/prosperity3bt/parse_submission_logs.py +87 -0
  7. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/round0/prices_round_0_day_-2.csv +1 -1
  8. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/runner.py +8 -7
  9. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/PKG-INFO +4 -2
  10. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/SOURCES.txt +2 -0
  11. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/pyproject.toml +9 -1
  12. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/LICENSE +0 -0
  13. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/__init__.py +0 -0
  14. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/datamodel.py +0 -0
  15. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/file_reader.py +0 -0
  16. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/models.py +0 -0
  17. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/__init__.py +0 -0
  18. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/round0/__init__.py +0 -0
  19. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/round0/trades_round_0_day_-2_nn.csv +0 -0
  20. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/dependency_links.txt +0 -0
  21. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/entry_points.txt +0 -0
  22. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/requires.txt +0 -0
  23. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/top_level.txt +0 -0
  24. {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: prosperity3bt
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Backtester for IMC Prosperity 3 algorithms
5
5
  Author-email: Jasper van Merle <jaspervmerle@gmail.com>
6
6
  License: MIT License
@@ -105,7 +105,9 @@ $ prosperity3bt example/starter.py 1 --no-trades-matching
105
105
 
106
106
  ## Order Matching
107
107
 
108
- Orders placed by `Trader.run` at a given timestamp are matched against the order depths and market trades of that timestamp's state. Order depths take priority, if an order can be filled completely using volume in the relevant order depth, market trades are not considered. If not, the backtester matches your order against the timestamp's market trades. In this case the backtester assumes that for each trade, the buyer and the seller of the trade are willing to trade with you instead at the trade's price and volume. Market trades are matched at the price of your orders, e.g. if you place a sell order for €9 and there is a market trade for €10, the sell order is matched at €9 (even though there is a buyer willing to pay €10, this appears to be consistent with what the official Prosperity environment does).
108
+ Orders placed by `Trader.run` at a given timestamp are matched against the order depths and market trades of that timestamp's state. Order depths take priority, if an order can be filled completely using volume in the relevant order depth, market trades are not considered. If not, the backtester matches your order against the timestamp's market trades, unless the `--no-trades-matching` flag is used.
109
+
110
+ In this case the backtester assumes that for each trade, the buyer and the seller of the trade are willing to trade with you instead at the trade's price and volume. Inspired by [team Linear Utility's Prosperity 2 write-up](https://github.com/ericcccsliu/imc-prosperity-2), a market trade is only matched if the trade's price is worse than your quotes. Market trades are matched at the price of your orders, e.g. if you place a sell order for €9 and there is a market trade for €10, the sell order is matched at €9 (even though there is a buyer willing to pay €10, this appears to be consistent with what the official Prosperity environment does).
109
111
 
110
112
  Limits are enforced before orders are matched to order depths. If for a product your position would exceed the limit, assuming all your orders would get filled, all your orders for that product get canceled.
111
113
 
@@ -61,7 +61,9 @@ $ prosperity3bt example/starter.py 1 --no-trades-matching
61
61
 
62
62
  ## Order Matching
63
63
 
64
- Orders placed by `Trader.run` at a given timestamp are matched against the order depths and market trades of that timestamp's state. Order depths take priority, if an order can be filled completely using volume in the relevant order depth, market trades are not considered. If not, the backtester matches your order against the timestamp's market trades. In this case the backtester assumes that for each trade, the buyer and the seller of the trade are willing to trade with you instead at the trade's price and volume. Market trades are matched at the price of your orders, e.g. if you place a sell order for €9 and there is a market trade for €10, the sell order is matched at €9 (even though there is a buyer willing to pay €10, this appears to be consistent with what the official Prosperity environment does).
64
+ Orders placed by `Trader.run` at a given timestamp are matched against the order depths and market trades of that timestamp's state. Order depths take priority, if an order can be filled completely using volume in the relevant order depth, market trades are not considered. If not, the backtester matches your order against the timestamp's market trades, unless the `--no-trades-matching` flag is used.
65
+
66
+ In this case the backtester assumes that for each trade, the buyer and the seller of the trade are willing to trade with you instead at the trade's price and volume. Inspired by [team Linear Utility's Prosperity 2 write-up](https://github.com/ericcccsliu/imc-prosperity-2), a market trade is only matched if the trade's price is worse than your quotes. Market trades are matched at the price of your orders, e.g. if you place a sell order for €9 and there is a market trade for €10, the sell order is matched at €9 (even though there is a buyer willing to pay €10, this appears to be consistent with what the official Prosperity environment does).
65
67
 
66
68
  Limits are enforced before orders are matched to order depths. If for a product your position would exceed the limit, assuming all your orders would get filled, all your orders for that product get canceled.
67
69
 
@@ -1,10 +1,8 @@
1
1
  import sys
2
- import webbrowser
3
2
  from argparse import ArgumentParser
4
3
  from collections import defaultdict
5
4
  from datetime import datetime
6
- from functools import partial, reduce
7
- from http.server import HTTPServer, SimpleHTTPRequestHandler
5
+ from functools import reduce
8
6
  from importlib import import_module, metadata, reload
9
7
  from pathlib import Path
10
8
  from typing import Any, Optional
@@ -12,6 +10,7 @@ from typing import Any, Optional
12
10
  from prosperity3bt.data import has_day_data
13
11
  from prosperity3bt.file_reader import FileReader, FileSystemReader, PackageResourcesReader
14
12
  from prosperity3bt.models import BacktestResult
13
+ from prosperity3bt.open import open_visualizer
15
14
  from prosperity3bt.runner import run_backtest
16
15
 
17
16
 
@@ -167,29 +166,6 @@ def print_overall_summary(results: list[BacktestResult]) -> None:
167
166
  print(f"Total profit: {total_profit:,.0f}")
168
167
 
169
168
 
170
- class HTTPRequestHandler(SimpleHTTPRequestHandler):
171
- def end_headers(self) -> None:
172
- self.send_header("Access-Control-Allow-Origin", "*")
173
- return super().end_headers()
174
-
175
- def log_message(self, format: str, *args: Any) -> None:
176
- return
177
-
178
-
179
- def open_visualizer(output_file: Path, no_requests: int) -> None:
180
- http_handler = partial(HTTPRequestHandler, directory=output_file.parent)
181
- http_server = HTTPServer(("localhost", 0), http_handler)
182
-
183
- webbrowser.open(
184
- f"https://jmerle.github.io/imc-prosperity-3-visualizer/?open=http://localhost:{http_server.server_port}/{output_file.name}"
185
- )
186
-
187
- # Chrome makes 2 requests: 1 OPTIONS request to check for CORS headers and 1 GET request to get the data
188
- # Some users reported their browser only makes 1 request, which is covered by the --vis-requests option
189
- for _ in range(no_requests):
190
- http_server.handle_request()
191
-
192
-
193
169
  def format_path(path: Path) -> str:
194
170
  cwd = Path.cwd()
195
171
  if path.is_relative_to(cwd):
@@ -200,7 +176,7 @@ def format_path(path: Path) -> str:
200
176
 
201
177
  def main() -> None:
202
178
  parser = ArgumentParser(prog="prosperity3bt", description="Run a backtest.")
203
- parser.add_argument("algorithm", type=str, help="path to the Python file containing the algoritm to backtest")
179
+ parser.add_argument("algorithm", type=str, help="path to the Python file containing the algorithm to backtest")
204
180
  parser.add_argument(
205
181
  "days",
206
182
  type=str,
@@ -221,12 +197,6 @@ def main() -> None:
221
197
  )
222
198
  parser.add_argument("--no-out", action="store_true", help="skip saving the output log to a file")
223
199
  parser.add_argument("--no-progress", action="store_true", help="don't show progress bars")
224
- parser.add_argument(
225
- "--vis-requests",
226
- type=int,
227
- default=2,
228
- help="number of requests the visualizer is expected to make to the backtester's HTTP server when using --vis",
229
- )
230
200
  parser.add_argument(
231
201
  "--original-timestamps",
232
202
  action="store_true",
@@ -294,8 +264,8 @@ def main() -> None:
294
264
  write_output(output_file, merged_results)
295
265
  print(f"\nSuccessfully saved backtest results to {format_path(output_file)}")
296
266
 
297
- if args.vis:
298
- open_visualizer(output_file, args.vis_requests)
267
+ if args.vis and output_file is not None:
268
+ open_visualizer(output_file)
299
269
 
300
270
 
301
271
  if __name__ == "__main__":
@@ -1,6 +1,5 @@
1
1
  from collections import defaultdict
2
2
  from dataclasses import dataclass
3
- from typing import Optional
4
3
 
5
4
  from prosperity3bt.datamodel import Symbol, Trade
6
5
  from prosperity3bt.file_reader import FileReader
@@ -45,7 +44,7 @@ class BacktestData:
45
44
  prices: dict[int, dict[Symbol, PriceRow]]
46
45
  trades: dict[int, dict[Symbol, list[Trade]]]
47
46
  products: list[Symbol]
48
- profit_loss: dict[Symbol, int]
47
+ profit_loss: dict[Symbol, float]
49
48
 
50
49
 
51
50
  def create_backtest_data(round_num: int, day_num: int, prices: list[PriceRow], trades: list[Trade]) -> BacktestData:
@@ -58,7 +57,7 @@ def create_backtest_data(round_num: int, day_num: int, prices: list[PriceRow], t
58
57
  trades_by_timestamp[trade.timestamp][trade.symbol].append(trade)
59
58
 
60
59
  products = sorted(set(row.product for row in prices))
61
- profit_loss = {product: 0 for product in products}
60
+ profit_loss = {product: 0.0 for product in products}
62
61
 
63
62
  return BacktestData(
64
63
  round_num=round_num,
@@ -75,11 +74,11 @@ def has_day_data(file_reader: FileReader, round_num: int, day_num: int) -> bool:
75
74
  return file is not None
76
75
 
77
76
 
78
- def read_day_data(file_reader: FileReader, round_num: int, day_num: int, no_names: bool) -> Optional[BacktestData]:
77
+ def read_day_data(file_reader: FileReader, round_num: int, day_num: int, no_names: bool) -> BacktestData:
79
78
  prices = []
80
79
  with file_reader.file([f"round{round_num}", f"prices_round_{round_num}_day_{day_num}.csv"]) as file:
81
80
  if file is None:
82
- return None
81
+ raise ValueError(f"Prices data is not available for round {round_num} day {day_num}")
83
82
 
84
83
  for line in file.read_text(encoding="utf-8").splitlines()[1:]:
85
84
  columns = line.split(";")
@@ -104,7 +103,8 @@ def read_day_data(file_reader: FileReader, round_num: int, day_num: int, no_name
104
103
  for suffix in trades_suffixes:
105
104
  with file_reader.file([f"round{round_num}", f"trades_round_{round_num}_day_{day_num}_{suffix}.csv"]) as file:
106
105
  if file is None:
107
- continue
106
+ trades_data_type = "Anonymized" if suffix == "nn" else "De-anonymized"
107
+ raise ValueError(f"{trades_data_type} trades data is not available for round {round_num} day {day_num}")
108
108
 
109
109
  for line in file.read_text(encoding="utf-8").splitlines()[1:]:
110
110
  columns = line.split(";")
@@ -0,0 +1,36 @@
1
+ import webbrowser
2
+ from functools import partial
3
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
4
+ from pathlib import Path
5
+ from typing import Any
6
+
7
+
8
+ class HTTPRequestHandler(SimpleHTTPRequestHandler):
9
+ def do_GET(self):
10
+ self.server.shutdown_flag = True
11
+ return super().do_GET()
12
+
13
+ def end_headers(self) -> None:
14
+ self.send_header("Access-Control-Allow-Origin", "*")
15
+ return super().end_headers()
16
+
17
+ def log_message(self, format: str, *args: Any) -> None:
18
+ return
19
+
20
+
21
+ class CustomHTTPServer(HTTPServer):
22
+ def __init__(self, *args, **kwargs) -> None:
23
+ super().__init__(*args, **kwargs)
24
+ self.shutdown_flag = False
25
+
26
+
27
+ def open_visualizer(output_file: Path) -> None:
28
+ http_handler = partial(HTTPRequestHandler, directory=str(output_file.parent))
29
+ http_server = CustomHTTPServer(("localhost", 0), http_handler)
30
+
31
+ webbrowser.open(
32
+ f"https://jmerle.github.io/imc-prosperity-3-visualizer/?open=http://localhost:{http_server.server_port}/{output_file.name}"
33
+ )
34
+
35
+ while not http_server.shutdown_flag:
36
+ http_server.handle_request()
@@ -0,0 +1,87 @@
1
+ import sys
2
+ from argparse import ArgumentParser
3
+ from pathlib import Path
4
+
5
+ import orjson
6
+
7
+
8
+ def parse_prices(activities_log: str, output_dir: Path, round_day: str) -> None:
9
+ output_file = output_dir / f"prices_{round_day}.csv"
10
+
11
+ print(f"Writing prices data to {output_file}")
12
+ with output_file.open("w+", encoding="utf-8") as f:
13
+ f.write(activities_log + "\n")
14
+
15
+
16
+ def parse_trades(trade_history: str, output_dir: Path, round_day: str) -> None:
17
+ trades = orjson.loads(trade_history)
18
+
19
+ with_names_options = [False]
20
+ if len(trades) > 0 and len(trades[0]["buyer"]) > 0 and len(trades[0]["seller"]) > 0:
21
+ with_names_options.append(True)
22
+
23
+ for with_names in with_names_options:
24
+ suffix = "wn" if with_names else "nn"
25
+ output_file = output_dir / f"trades_{round_day}_{suffix}.csv"
26
+
27
+ print(f"Writing trades data to {output_file}")
28
+ with output_file.open("w+", encoding="utf-8") as f:
29
+ f.write("timestamp;buyer;seller;symbol;currency;price;quantity\n")
30
+
31
+ for t in trades:
32
+ row = ";".join(
33
+ [
34
+ str(t["timestamp"]),
35
+ t["buyer"] if with_names else "",
36
+ t["seller"] if with_names else "",
37
+ t["symbol"],
38
+ t["currency"],
39
+ str(t["price"]),
40
+ str(t["quantity"]),
41
+ ]
42
+ )
43
+
44
+ f.write(row + "\n")
45
+
46
+
47
+ def main() -> None:
48
+ parser = ArgumentParser(
49
+ description="Save prices and trades data in submission logs to prosperity3bt's resources module.",
50
+ )
51
+ parser.add_argument("file", type=str, help="path to the log file")
52
+ parser.add_argument("round", type=int, help="round the logs belong to")
53
+ parser.add_argument("day", type=int, help="day the logs belong to")
54
+
55
+ args = parser.parse_args()
56
+
57
+ file = Path(args.file).expanduser().resolve()
58
+ if not file.is_file():
59
+ print(f"Error: {file} is not a file")
60
+ sys.exit(1)
61
+
62
+ logs = file.read_text()
63
+
64
+ sections = {}
65
+ for block in logs.split("\n\n"):
66
+ block = block.strip()
67
+ if len(block) == 0:
68
+ continue
69
+
70
+ newline_idx = block.index("\n")
71
+ category = block[: newline_idx - 1]
72
+ content = block[newline_idx + 1 :]
73
+
74
+ sections[category] = content
75
+
76
+ output_dir = Path(__file__).parent / "resources" / f"round{args.round}"
77
+ if not output_dir.is_dir():
78
+ output_dir.mkdir(parents=True)
79
+
80
+ round_day = f"round_{args.round}_day_{args.day}"
81
+
82
+ parse_prices(sections["Activities log"], output_dir, round_day)
83
+ parse_trades(sections["Trade History"], output_dir, round_day)
84
+
85
+
86
+ if __name__ == "__main__":
87
+ main()
@@ -2070,7 +2070,7 @@ day;timestamp;product;bid_price_1;bid_volume_1;bid_price_2;bid_volume_2;bid_pric
2070
2070
  -1;103400;KELP;2018;32;;;;;2019;1;2020;5;2022;32;2018.5;0.0
2071
2071
  -1;103400;RAINFOREST_RESIN;9998;9;9996;2;9995;30;10000;4;10004;2;10005;30;9999.0;0.0
2072
2072
  -1;103500;RAINFOREST_RESIN;9996;1;9995;27;;;10004;1;10005;27;;;10000.0;0.0
2073
- -1;103500;KELP;2019;8;2018;28;;;2020;18;2022;28;;;2019.5;0.0
2073
+ -1;103500;KELP;2019;8;2018;28;;;2020;9;2022;28;;;2019.5;0.0
2074
2074
  -1;103600;RAINFOREST_RESIN;9996;2;9995;25;;;10004;2;10005;25;;;10000.0;0.0
2075
2075
  -1;103600;KELP;2018;27;;;;;2022;27;;;;;2020.0;0.0
2076
2076
  -1;103700;KELP;2019;1;2018;24;;;2022;25;;;;;2020.5;0.0
@@ -25,7 +25,7 @@ def prepare_state(state: TradingState, data: BacktestData) -> None:
25
25
 
26
26
  state.order_depths[product] = order_depth
27
27
 
28
- state.listings[product] = {
28
+ state.listings[product] = { # type: ignore[assignment]
29
29
  "symbol": product,
30
30
  "product": product,
31
31
  "denomination": 1,
@@ -120,7 +120,7 @@ def match_buy_order(
120
120
  return trades
121
121
 
122
122
  for market_trade in market_trades:
123
- if market_trade.sell_quantity == 0 or market_trade.trade.price > order.price:
123
+ if market_trade.sell_quantity == 0 or market_trade.trade.price >= order.price:
124
124
  continue
125
125
 
126
126
  volume = min(order.quantity, market_trade.sell_quantity)
@@ -165,7 +165,7 @@ def match_sell_order(
165
165
  return trades
166
166
 
167
167
  for market_trade in market_trades:
168
- if market_trade.buy_quantity == 0 or market_trade.trade.price < order.price:
168
+ if market_trade.buy_quantity == 0 or market_trade.trade.price <= order.price:
169
169
  continue
170
170
 
171
171
  volume = min(abs(order.quantity), market_trade.buy_quantity)
@@ -200,9 +200,10 @@ def match_orders(
200
200
  result: BacktestResult,
201
201
  disable_trades_matching: bool,
202
202
  ) -> None:
203
- market_trades: dict[Symbol, list[MarketTrade]] = {}
204
- for product, trades in data.trades[state.timestamp].items():
205
- market_trades[product] = [MarketTrade(t, t.quantity, t.quantity) for t in trades]
203
+ market_trades = {
204
+ product: [MarketTrade(t, t.quantity, t.quantity) for t in trades]
205
+ for product, trades in data.trades[state.timestamp].items()
206
+ }
206
207
 
207
208
  for product in data.products:
208
209
  new_trades = []
@@ -279,7 +280,7 @@ def run_backtest(
279
280
 
280
281
  # Tee calls stdout.close(), making stdout.getvalue() impossible
281
282
  # This override makes getvalue() possible after close()
282
- stdout.close = lambda: None
283
+ stdout.close = lambda: None # type: ignore[method-assign]
283
284
 
284
285
  if print_output:
285
286
  with closing(Tee(stdout)):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: prosperity3bt
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Backtester for IMC Prosperity 3 algorithms
5
5
  Author-email: Jasper van Merle <jaspervmerle@gmail.com>
6
6
  License: MIT License
@@ -105,7 +105,9 @@ $ prosperity3bt example/starter.py 1 --no-trades-matching
105
105
 
106
106
  ## Order Matching
107
107
 
108
- Orders placed by `Trader.run` at a given timestamp are matched against the order depths and market trades of that timestamp's state. Order depths take priority, if an order can be filled completely using volume in the relevant order depth, market trades are not considered. If not, the backtester matches your order against the timestamp's market trades. In this case the backtester assumes that for each trade, the buyer and the seller of the trade are willing to trade with you instead at the trade's price and volume. Market trades are matched at the price of your orders, e.g. if you place a sell order for €9 and there is a market trade for €10, the sell order is matched at €9 (even though there is a buyer willing to pay €10, this appears to be consistent with what the official Prosperity environment does).
108
+ Orders placed by `Trader.run` at a given timestamp are matched against the order depths and market trades of that timestamp's state. Order depths take priority, if an order can be filled completely using volume in the relevant order depth, market trades are not considered. If not, the backtester matches your order against the timestamp's market trades, unless the `--no-trades-matching` flag is used.
109
+
110
+ In this case the backtester assumes that for each trade, the buyer and the seller of the trade are willing to trade with you instead at the trade's price and volume. Inspired by [team Linear Utility's Prosperity 2 write-up](https://github.com/ericcccsliu/imc-prosperity-2), a market trade is only matched if the trade's price is worse than your quotes. Market trades are matched at the price of your orders, e.g. if you place a sell order for €9 and there is a market trade for €10, the sell order is matched at €9 (even though there is a buyer willing to pay €10, this appears to be consistent with what the official Prosperity environment does).
109
111
 
110
112
  Limits are enforced before orders are matched to order depths. If for a product your position would exceed the limit, assuming all your orders would get filled, all your orders for that product get canceled.
111
113
 
@@ -7,6 +7,8 @@ prosperity3bt/data.py
7
7
  prosperity3bt/datamodel.py
8
8
  prosperity3bt/file_reader.py
9
9
  prosperity3bt/models.py
10
+ prosperity3bt/open.py
11
+ prosperity3bt/parse_submission_logs.py
10
12
  prosperity3bt/runner.py
11
13
  prosperity3bt.egg-info/PKG-INFO
12
14
  prosperity3bt.egg-info/SOURCES.txt
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "prosperity3bt"
3
3
  description = "Backtester for IMC Prosperity 3 algorithms"
4
- version = "0.1.0"
4
+ version = "0.2.0"
5
5
  readme = "README.md"
6
6
  license = {file = "LICENSE"}
7
7
  authors = [{name = "Jasper van Merle", email = "jaspervmerle@gmail.com"}]
@@ -36,6 +36,7 @@ prosperity3bt = ["resources/*/*.csv"]
36
36
 
37
37
  [tool.uv]
38
38
  dev-dependencies = [
39
+ "mypy>=1.15.0",
39
40
  "ruff>=0.9.7",
40
41
  ]
41
42
 
@@ -44,3 +45,10 @@ line-length = 120
44
45
 
45
46
  [tool.ruff.lint]
46
47
  extend-select = ["I"]
48
+
49
+ [tool.mypy]
50
+ ignore_missing_imports = true
51
+
52
+ [[tool.mypy.overrides]]
53
+ module = "prosperity3bt.datamodel"
54
+ ignore_errors = true
File without changes
File without changes