prosperity3bt 0.2.0__tar.gz → 0.4.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 (26) hide show
  1. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/PKG-INFO +13 -11
  2. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/README.md +5 -5
  3. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/__main__.py +55 -69
  4. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/datamodel.py +1 -1
  5. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/models.py +7 -0
  6. prosperity3bt-0.4.0/prosperity3bt/resources/round0/__init__.py +0 -0
  7. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/runner.py +51 -20
  8. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt.egg-info/PKG-INFO +13 -11
  9. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt.egg-info/SOURCES.txt +1 -0
  10. prosperity3bt-0.4.0/prosperity3bt.egg-info/requires.txt +5 -0
  11. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/pyproject.toml +8 -2
  12. prosperity3bt-0.2.0/prosperity3bt.egg-info/requires.txt +0 -4
  13. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/LICENSE +0 -0
  14. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/__init__.py +0 -0
  15. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/data.py +0 -0
  16. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/file_reader.py +0 -0
  17. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/open.py +0 -0
  18. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/parse_submission_logs.py +0 -0
  19. /prosperity3bt-0.2.0/prosperity3bt/resources/__init__.py → /prosperity3bt-0.4.0/prosperity3bt/py.typed +0 -0
  20. {prosperity3bt-0.2.0/prosperity3bt/resources/round0 → prosperity3bt-0.4.0/prosperity3bt/resources}/__init__.py +0 -0
  21. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/resources/round0/prices_round_0_day_-2.csv +0 -0
  22. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt/resources/round0/trades_round_0_day_-2_nn.csv +0 -0
  23. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt.egg-info/dependency_links.txt +0 -0
  24. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt.egg-info/entry_points.txt +0 -0
  25. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/prosperity3bt.egg-info/top_level.txt +0 -0
  26. {prosperity3bt-0.2.0 → prosperity3bt-0.4.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: prosperity3bt
3
- Version: 0.2.0
3
+ Version: 0.4.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
@@ -37,10 +37,12 @@ Classifier: Programming Language :: Python :: 3
37
37
  Requires-Python: >=3.9
38
38
  Description-Content-Type: text/markdown
39
39
  License-File: LICENSE
40
- Requires-Dist: ipython
41
- Requires-Dist: jsonpickle
42
- Requires-Dist: orjson
43
- Requires-Dist: tqdm
40
+ Requires-Dist: ipython>=8.18.1
41
+ Requires-Dist: jsonpickle>=4.0.2
42
+ Requires-Dist: orjson>=3.10.15
43
+ Requires-Dist: tqdm>=4.67.1
44
+ Requires-Dist: typer>=0.15.2
45
+ Dynamic: license-file
44
46
 
45
47
  # IMC Prosperity 3 Backtester
46
48
 
@@ -98,16 +100,16 @@ $ prosperity3bt example/starter.py 1 --data prosperity3bt/resources
98
100
  # Print trader's output to stdout while running
99
101
  # This may be helpful when debugging a broken trader
100
102
  $ prosperity3bt example/starter.py 1 --print
101
-
102
- # Only match orders against order depths, not against market trades
103
- $ prosperity3bt example/starter.py 1 --no-trades-matching
104
103
  ```
105
104
 
106
105
  ## Order Matching
107
106
 
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.
107
+ 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).
109
108
 
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
+ Matching orders against market trades can be configured through the `--match-trades` option:
110
+ - `--match-trades all` (default): match market trades with prices equal to or worse than your quotes.
111
+ - `--match-trades worse`: match market trades with prices worse than your quotes, inspired by [team Linear Utility's Prosperity 2 write-up](https://github.com/ericcccsliu/imc-prosperity-2).
112
+ - `--match-trades none`: do not match market trades against orders.
111
113
 
112
114
  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.
113
115
 
@@ -54,16 +54,16 @@ $ prosperity3bt example/starter.py 1 --data prosperity3bt/resources
54
54
  # Print trader's output to stdout while running
55
55
  # This may be helpful when debugging a broken trader
56
56
  $ prosperity3bt example/starter.py 1 --print
57
-
58
- # Only match orders against order depths, not against market trades
59
- $ prosperity3bt example/starter.py 1 --no-trades-matching
60
57
  ```
61
58
 
62
59
  ## Order Matching
63
60
 
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.
61
+ 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).
65
62
 
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).
63
+ Matching orders against market trades can be configured through the `--match-trades` option:
64
+ - `--match-trades all` (default): match market trades with prices equal to or worse than your quotes.
65
+ - `--match-trades worse`: match market trades with prices worse than your quotes, inspired by [team Linear Utility's Prosperity 2 write-up](https://github.com/ericcccsliu/imc-prosperity-2).
66
+ - `--match-trades none`: do not match market trades against orders.
67
67
 
68
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.
69
69
 
@@ -1,31 +1,28 @@
1
1
  import sys
2
- from argparse import ArgumentParser
3
2
  from collections import defaultdict
4
3
  from datetime import datetime
5
4
  from functools import reduce
6
5
  from importlib import import_module, metadata, reload
7
6
  from pathlib import Path
8
- from typing import Any, Optional
7
+ from typing import Annotated, Any, Optional
8
+
9
+ from typer import Argument, Option, Typer
9
10
 
10
11
  from prosperity3bt.data import has_day_data
11
12
  from prosperity3bt.file_reader import FileReader, FileSystemReader, PackageResourcesReader
12
- from prosperity3bt.models import BacktestResult
13
+ from prosperity3bt.models import BacktestResult, TradeMatchingMode
13
14
  from prosperity3bt.open import open_visualizer
14
15
  from prosperity3bt.runner import run_backtest
15
16
 
16
17
 
17
- def parse_algorithm(algorithm: str) -> Any:
18
- algorithm_path = Path(algorithm).expanduser().resolve()
19
- if not algorithm_path.is_file():
20
- raise ModuleNotFoundError(f"{algorithm_path} is not a file")
21
-
22
- sys.path.append(str(algorithm_path.parent))
23
- return import_module(algorithm_path.stem)
18
+ def parse_algorithm(algorithm: Path) -> Any:
19
+ sys.path.append(str(algorithm.parent))
20
+ return import_module(algorithm.stem)
24
21
 
25
22
 
26
- def parse_data(data_root: Optional[str]) -> FileReader:
23
+ def parse_data(data_root: Optional[Path]) -> FileReader:
27
24
  if data_root is not None:
28
- return FileSystemReader(Path(data_root).expanduser().resolve())
25
+ return FileSystemReader(data_root)
29
26
  else:
30
27
  return PackageResourcesReader()
31
28
 
@@ -63,9 +60,9 @@ def parse_days(file_reader: FileReader, days: list[str]) -> list[tuple[int, int]
63
60
  return parsed_days
64
61
 
65
62
 
66
- def parse_out(out: Optional[str], no_out: bool) -> Optional[Path]:
63
+ def parse_out(out: Optional[Path], no_out: bool) -> Optional[Path]:
67
64
  if out is not None:
68
- return Path(out).expanduser().resolve()
65
+ return out
69
66
 
70
67
  if no_out:
71
68
  return None
@@ -174,68 +171,53 @@ def format_path(path: Path) -> str:
174
171
  return str(path)
175
172
 
176
173
 
177
- def main() -> None:
178
- parser = ArgumentParser(prog="prosperity3bt", description="Run a backtest.")
179
- parser.add_argument("algorithm", type=str, help="path to the Python file containing the algorithm to backtest")
180
- parser.add_argument(
181
- "days",
182
- type=str,
183
- nargs="+",
184
- help="the days to backtest on (<round>-<day> for a single day, <round> for all days in a round)",
185
- )
186
- parser.add_argument("--merge-pnl", action="store_true", help="merge profit and loss across days")
187
- parser.add_argument("--vis", action="store_true", help="open backtest result in visualizer when done")
188
- parser.add_argument("--out", type=str, help="path to save output log to (defaults to backtests/<timestamp>.log)")
189
- parser.add_argument(
190
- "--data",
191
- type=str,
192
- help="path to data directory (must look similar in structure to https://github.com/jmerle/imc-prosperity-3-backtester/tree/master/prosperity3bt/resources)",
193
- )
194
- parser.add_argument("--print", action="store_true", help="print the trader's output to stdout while it's running")
195
- parser.add_argument(
196
- "--no-trades-matching", action="store_true", help="disable matching orders against market trades"
197
- )
198
- parser.add_argument("--no-out", action="store_true", help="skip saving the output log to a file")
199
- parser.add_argument("--no-progress", action="store_true", help="don't show progress bars")
200
- parser.add_argument(
201
- "--original-timestamps",
202
- action="store_true",
203
- help="preserve original timestamps in output log rather than making them increase across days",
204
- )
205
- # parser.add_argument(
206
- # "--no-names", action="store_true", help="don't use de-anonymized trades data, even if it exists"
207
- # )
208
- parser.add_argument("-v", "--version", action="version", version=f"%(prog)s {metadata.version(__package__)}")
209
-
210
- args = parser.parse_args()
211
-
212
- if args.vis and args.no_out:
213
- print("Error: --vis and --no-out are mutually exclusive")
214
- sys.exit(1)
174
+ def version_callback(value: bool) -> None:
175
+ if value:
176
+ print(f"prosperity3bt {metadata.version(__package__)}")
177
+ sys.exit(0)
178
+
215
179
 
216
- if args.out is not None and args.no_out:
180
+ app = Typer(context_settings={"help_option_names": ["--help", "-h"]})
181
+
182
+
183
+ @app.command()
184
+ def cli(
185
+ algorithm: Annotated[Path, Argument(help="Path to the Python file containing the algorithm to backtest.", show_default=False, exists=True, file_okay=True, dir_okay=False, resolve_path=True)],
186
+ days: Annotated[list[str], Argument(help="The days to backtest on. <round>-<day> for a single day, <round> for all days in a round.", show_default=False)],
187
+ merge_pnl: Annotated[bool, Option("--merge-pnl", help="Merge profit and loss across days.")] = False,
188
+ vis: Annotated[bool, Option("--vis", help="Open backtest results in https://jmerle.github.io/imc-prosperity-3-visualizer/ when done.")] = False,
189
+ out: Annotated[Optional[Path], Option(help="File to save output log to (defaults to backtests/<timestamp>.log).", show_default=False, dir_okay=False, resolve_path=True)] = None,
190
+ no_out: Annotated[bool, Option("--no-out", help="Skip saving output log.")] = False,
191
+ data: Annotated[Optional[Path], Option(help="Path to data directory. Must look similar in structure to https://github.com/jmerle/imc-prosperity-3-backtester/tree/master/prosperity3bt/resources.", show_default=False, exists=True, file_okay=False, dir_okay=True, resolve_path=True)] = None,
192
+ print_output: Annotated[bool, Option("--print", help="Print the trader's output to stdout while it's running.")] = False,
193
+ match_trades: Annotated[TradeMatchingMode, Option(help="How to match orders against market trades. 'all' matches trades with prices equal to or worse than your quotes, 'worse' matches trades with prices worse than your quotes, 'none' does not match trades against orders at all.")] = TradeMatchingMode.all,
194
+ no_progress: Annotated[bool, Option("--no-progress", help="Don't show progress bars.")] = False,
195
+ original_timestamps: Annotated[bool, Option("--original-timestamps", help="Preserve original timestamps in output log rather than making them increase across days.")] = False,
196
+ version: Annotated[bool, Option("--version", "-v", help="Show the program's version number and exit.", is_eager=True, callback=version_callback)] = False,
197
+ ) -> None: # fmt: skip
198
+ if out is not None and no_out:
217
199
  print("Error: --out and --no-out are mutually exclusive")
218
200
  sys.exit(1)
219
201
 
220
202
  try:
221
- trader_module = parse_algorithm(args.algorithm)
203
+ trader_module = parse_algorithm(algorithm)
222
204
  except ModuleNotFoundError as e:
223
- print(f"{args.algorithm} is not a valid algorithm file: {e}")
205
+ print(f"{algorithm} is not a valid algorithm file: {e}")
224
206
  sys.exit(1)
225
207
 
226
208
  if not hasattr(trader_module, "Trader"):
227
- print(f"{args.algorithm} does not expose a Trader class")
209
+ print(f"{algorithm} does not expose a Trader class")
228
210
  sys.exit(1)
229
211
 
230
- file_reader = parse_data(args.data)
231
- days = parse_days(file_reader, args.days)
232
- output_file = parse_out(args.out, args.no_out)
212
+ file_reader = parse_data(data)
213
+ parsed_days = parse_days(file_reader, days)
214
+ output_file = parse_out(out, no_out)
233
215
 
234
- show_progress_bars = not args.no_progress and not args.print
216
+ show_progress_bars = not no_progress and not print_output
235
217
 
236
218
  results = []
237
- for round_num, day_num in days:
238
- print(f"Backtesting {args.algorithm} on round {round_num} day {day_num}")
219
+ for round_num, day_num in parsed_days:
220
+ print(f"Backtesting {algorithm} on round {round_num} day {day_num}")
239
221
 
240
222
  reload(trader_module)
241
223
 
@@ -244,29 +226,33 @@ def main() -> None:
244
226
  file_reader,
245
227
  round_num,
246
228
  day_num,
247
- args.print,
248
- args.no_trades_matching,
249
- True, # args.no_names,
229
+ print_output,
230
+ match_trades,
231
+ True,
250
232
  show_progress_bars,
251
233
  )
252
234
 
253
235
  print_day_summary(result)
254
- if len(days) > 1:
236
+ if len(parsed_days) > 1:
255
237
  print()
256
238
 
257
239
  results.append(result)
258
240
 
259
- if len(days) > 1:
241
+ if len(parsed_days) > 1:
260
242
  print_overall_summary(results)
261
243
 
262
244
  if output_file is not None:
263
- merged_results = reduce(lambda a, b: merge_results(a, b, args.merge_pnl, not args.original_timestamps), results)
245
+ merged_results = reduce(lambda a, b: merge_results(a, b, merge_pnl, not original_timestamps), results)
264
246
  write_output(output_file, merged_results)
265
247
  print(f"\nSuccessfully saved backtest results to {format_path(output_file)}")
266
248
 
267
- if args.vis and output_file is not None:
249
+ if vis and output_file is not None:
268
250
  open_visualizer(output_file)
269
251
 
270
252
 
253
+ def main() -> None:
254
+ app()
255
+
256
+
271
257
  if __name__ == "__main__":
272
258
  main()
@@ -13,7 +13,7 @@ ObservationValue = int
13
13
 
14
14
 
15
15
  class Listing:
16
- def __init__(self, symbol: Symbol, product: Product, denomination: Product):
16
+ def __init__(self, symbol: Symbol, product: Product, denomination: int):
17
17
  self.symbol = symbol
18
18
  self.product = product
19
19
  self.denomination = denomination
@@ -1,4 +1,5 @@
1
1
  from dataclasses import dataclass
2
+ from enum import Enum
2
3
  from typing import Any
3
4
 
4
5
  import orjson
@@ -101,3 +102,9 @@ class MarketTrade:
101
102
  trade: Trade
102
103
  buy_quantity: int
103
104
  sell_quantity: int
105
+
106
+
107
+ class TradeMatchingMode(str, Enum):
108
+ all = "all"
109
+ worse = "worse"
110
+ none = "none"
@@ -1,15 +1,21 @@
1
1
  import os
2
2
  from contextlib import closing, redirect_stdout
3
3
  from io import StringIO
4
- from typing import Any
5
4
 
6
5
  from IPython.utils.io import Tee
7
6
  from tqdm import tqdm
8
7
 
9
8
  from prosperity3bt.data import LIMITS, BacktestData, read_day_data
10
- from prosperity3bt.datamodel import Observation, Order, OrderDepth, Symbol, Trade, TradingState
9
+ from prosperity3bt.datamodel import Listing, Observation, Order, OrderDepth, Symbol, Trade, TradingState
11
10
  from prosperity3bt.file_reader import FileReader
12
- from prosperity3bt.models import ActivityLogRow, BacktestResult, MarketTrade, SandboxLogRow, TradeRow
11
+ from prosperity3bt.models import (
12
+ ActivityLogRow,
13
+ BacktestResult,
14
+ MarketTrade,
15
+ SandboxLogRow,
16
+ TradeMatchingMode,
17
+ TradeRow,
18
+ )
13
19
 
14
20
 
15
21
  def prepare_state(state: TradingState, data: BacktestData) -> None:
@@ -25,11 +31,7 @@ def prepare_state(state: TradingState, data: BacktestData) -> None:
25
31
 
26
32
  state.order_depths[product] = order_depth
27
33
 
28
- state.listings[product] = { # type: ignore[assignment]
29
- "symbol": product,
30
- "product": product,
31
- "denomination": 1,
32
- }
34
+ state.listings[product] = Listing(product, product, 1)
33
35
 
34
36
 
35
37
  def create_activity_logs(
@@ -97,7 +99,11 @@ def enforce_limits(
97
99
 
98
100
 
99
101
  def match_buy_order(
100
- state: TradingState, data: BacktestData, order: Order, market_trades: list[MarketTrade]
102
+ state: TradingState,
103
+ data: BacktestData,
104
+ order: Order,
105
+ market_trades: list[MarketTrade],
106
+ trade_matching_mode: TradeMatchingMode,
101
107
  ) -> list[Trade]:
102
108
  trades = []
103
109
 
@@ -119,8 +125,15 @@ def match_buy_order(
119
125
  if order.quantity == 0:
120
126
  return trades
121
127
 
128
+ if trade_matching_mode == TradeMatchingMode.none:
129
+ return trades
130
+
122
131
  for market_trade in market_trades:
123
- if market_trade.sell_quantity == 0 or market_trade.trade.price >= order.price:
132
+ if (
133
+ market_trade.sell_quantity == 0
134
+ or market_trade.trade.price > order.price
135
+ or (market_trade.trade.price == order.price and trade_matching_mode == TradeMatchingMode.worse)
136
+ ):
124
137
  continue
125
138
 
126
139
  volume = min(order.quantity, market_trade.sell_quantity)
@@ -142,7 +155,11 @@ def match_buy_order(
142
155
 
143
156
 
144
157
  def match_sell_order(
145
- state: TradingState, data: BacktestData, order: Order, market_trades: list[MarketTrade]
158
+ state: TradingState,
159
+ data: BacktestData,
160
+ order: Order,
161
+ market_trades: list[MarketTrade],
162
+ trade_matching_mode: TradeMatchingMode,
146
163
  ) -> list[Trade]:
147
164
  trades = []
148
165
 
@@ -164,8 +181,15 @@ def match_sell_order(
164
181
  if order.quantity == 0:
165
182
  return trades
166
183
 
184
+ if trade_matching_mode == TradeMatchingMode.none:
185
+ return trades
186
+
167
187
  for market_trade in market_trades:
168
- if market_trade.buy_quantity == 0 or market_trade.trade.price <= order.price:
188
+ if (
189
+ market_trade.buy_quantity == 0
190
+ or market_trade.trade.price < order.price
191
+ or (market_trade.trade.price == order.price and trade_matching_mode == TradeMatchingMode.worse)
192
+ ):
169
193
  continue
170
194
 
171
195
  volume = min(abs(order.quantity), market_trade.buy_quantity)
@@ -184,11 +208,17 @@ def match_sell_order(
184
208
  return trades
185
209
 
186
210
 
187
- def match_order(state: TradingState, data: BacktestData, order: Order, market_trades: list[MarketTrade]) -> list[Trade]:
211
+ def match_order(
212
+ state: TradingState,
213
+ data: BacktestData,
214
+ order: Order,
215
+ market_trades: list[MarketTrade],
216
+ trade_matching_mode: TradeMatchingMode,
217
+ ) -> list[Trade]:
188
218
  if order.quantity > 0:
189
- return match_buy_order(state, data, order, market_trades)
219
+ return match_buy_order(state, data, order, market_trades, trade_matching_mode)
190
220
  elif order.quantity < 0:
191
- return match_sell_order(state, data, order, market_trades)
221
+ return match_sell_order(state, data, order, market_trades, trade_matching_mode)
192
222
  else:
193
223
  return []
194
224
 
@@ -198,7 +228,7 @@ def match_orders(
198
228
  data: BacktestData,
199
229
  orders: dict[Symbol, list[Order]],
200
230
  result: BacktestResult,
201
- disable_trades_matching: bool,
231
+ trade_matching_mode: TradeMatchingMode,
202
232
  ) -> None:
203
233
  market_trades = {
204
234
  product: [MarketTrade(t, t.quantity, t.quantity) for t in trades]
@@ -214,7 +244,8 @@ def match_orders(
214
244
  state,
215
245
  data,
216
246
  order,
217
- [] if disable_trades_matching else market_trades.get(product, []),
247
+ market_trades.get(product, []),
248
+ trade_matching_mode,
218
249
  )
219
250
  )
220
251
 
@@ -233,12 +264,12 @@ def match_orders(
233
264
 
234
265
 
235
266
  def run_backtest(
236
- trader: Any,
267
+ trader,
237
268
  file_reader: FileReader,
238
269
  round_num: int,
239
270
  day_num: int,
240
271
  print_output: bool,
241
- disable_trades_matching: bool,
272
+ trade_matching_mode: TradeMatchingMode,
242
273
  no_names: bool,
243
274
  show_progress_bar: bool,
244
275
  ) -> BacktestResult:
@@ -299,6 +330,6 @@ def run_backtest(
299
330
 
300
331
  create_activity_logs(state, data, result)
301
332
  enforce_limits(state, data, orders, sandbox_row)
302
- match_orders(state, data, orders, result, disable_trades_matching)
333
+ match_orders(state, data, orders, result, trade_matching_mode)
303
334
 
304
335
  return result
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: prosperity3bt
3
- Version: 0.2.0
3
+ Version: 0.4.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
@@ -37,10 +37,12 @@ Classifier: Programming Language :: Python :: 3
37
37
  Requires-Python: >=3.9
38
38
  Description-Content-Type: text/markdown
39
39
  License-File: LICENSE
40
- Requires-Dist: ipython
41
- Requires-Dist: jsonpickle
42
- Requires-Dist: orjson
43
- Requires-Dist: tqdm
40
+ Requires-Dist: ipython>=8.18.1
41
+ Requires-Dist: jsonpickle>=4.0.2
42
+ Requires-Dist: orjson>=3.10.15
43
+ Requires-Dist: tqdm>=4.67.1
44
+ Requires-Dist: typer>=0.15.2
45
+ Dynamic: license-file
44
46
 
45
47
  # IMC Prosperity 3 Backtester
46
48
 
@@ -98,16 +100,16 @@ $ prosperity3bt example/starter.py 1 --data prosperity3bt/resources
98
100
  # Print trader's output to stdout while running
99
101
  # This may be helpful when debugging a broken trader
100
102
  $ prosperity3bt example/starter.py 1 --print
101
-
102
- # Only match orders against order depths, not against market trades
103
- $ prosperity3bt example/starter.py 1 --no-trades-matching
104
103
  ```
105
104
 
106
105
  ## Order Matching
107
106
 
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.
107
+ 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).
109
108
 
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
+ Matching orders against market trades can be configured through the `--match-trades` option:
110
+ - `--match-trades all` (default): match market trades with prices equal to or worse than your quotes.
111
+ - `--match-trades worse`: match market trades with prices worse than your quotes, inspired by [team Linear Utility's Prosperity 2 write-up](https://github.com/ericcccsliu/imc-prosperity-2).
112
+ - `--match-trades none`: do not match market trades against orders.
111
113
 
112
114
  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.
113
115
 
@@ -9,6 +9,7 @@ prosperity3bt/file_reader.py
9
9
  prosperity3bt/models.py
10
10
  prosperity3bt/open.py
11
11
  prosperity3bt/parse_submission_logs.py
12
+ prosperity3bt/py.typed
12
13
  prosperity3bt/runner.py
13
14
  prosperity3bt.egg-info/PKG-INFO
14
15
  prosperity3bt.egg-info/SOURCES.txt
@@ -0,0 +1,5 @@
1
+ ipython>=8.18.1
2
+ jsonpickle>=4.0.2
3
+ orjson>=3.10.15
4
+ tqdm>=4.67.1
5
+ typer>=0.15.2
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "prosperity3bt"
3
3
  description = "Backtester for IMC Prosperity 3 algorithms"
4
- version = "0.2.0"
4
+ version = "0.4.0"
5
5
  readme = "README.md"
6
6
  license = {file = "LICENSE"}
7
7
  authors = [{name = "Jasper van Merle", email = "jaspervmerle@gmail.com"}]
@@ -14,7 +14,13 @@ classifiers = [
14
14
  "Programming Language :: Python :: 3",
15
15
  ]
16
16
  requires-python = ">= 3.9"
17
- dependencies = ["ipython", "jsonpickle", "orjson", "tqdm"]
17
+ dependencies = [
18
+ "ipython>=8.18.1",
19
+ "jsonpickle>=4.0.2",
20
+ "orjson>=3.10.15",
21
+ "tqdm>=4.67.1",
22
+ "typer>=0.15.2",
23
+ ]
18
24
 
19
25
  [build-system]
20
26
  requires = ["setuptools >= 61.0"]
@@ -1,4 +0,0 @@
1
- ipython
2
- jsonpickle
3
- orjson
4
- tqdm
File without changes
File without changes