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.
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/PKG-INFO +4 -2
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/README.md +3 -1
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/__main__.py +5 -35
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/data.py +6 -6
- prosperity3bt-0.2.0/prosperity3bt/open.py +36 -0
- prosperity3bt-0.2.0/prosperity3bt/parse_submission_logs.py +87 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/round0/prices_round_0_day_-2.csv +1 -1
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/runner.py +8 -7
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/PKG-INFO +4 -2
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/SOURCES.txt +2 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/pyproject.toml +9 -1
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/LICENSE +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/__init__.py +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/datamodel.py +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/file_reader.py +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/models.py +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/__init__.py +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/round0/__init__.py +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/round0/trades_round_0_day_-2_nn.csv +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/dependency_links.txt +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/entry_points.txt +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/requires.txt +0 -0
- {prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt.egg-info/top_level.txt +0 -0
- {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.
|
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
|
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
|
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
|
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
|
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
|
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,
|
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) ->
|
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
|
-
|
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
|
-
|
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()
|
{prosperity3bt-0.1.0 → prosperity3bt-0.2.0}/prosperity3bt/resources/round0/prices_round_0_day_-2.csv
RENAMED
@@ -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;
|
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
|
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
|
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
|
204
|
-
|
205
|
-
|
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.
|
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
|
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.
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|