prosperity3bt 0.1.0__py3-none-any.whl
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/__init__.py +0 -0
- prosperity3bt/__main__.py +302 -0
- prosperity3bt/data.py +125 -0
- prosperity3bt/datamodel.py +153 -0
- prosperity3bt/file_reader.py +44 -0
- prosperity3bt/models.py +103 -0
- prosperity3bt/resources/__init__.py +0 -0
- prosperity3bt/resources/round0/__init__.py +0 -0
- prosperity3bt/resources/round0/prices_round_0_day_-2.csv +4001 -0
- prosperity3bt/resources/round0/trades_round_0_day_-2_nn.csv +1089 -0
- prosperity3bt/runner.py +303 -0
- prosperity3bt-0.1.0.dist-info/LICENSE +21 -0
- prosperity3bt-0.1.0.dist-info/METADATA +129 -0
- prosperity3bt-0.1.0.dist-info/RECORD +17 -0
- prosperity3bt-0.1.0.dist-info/WHEEL +5 -0
- prosperity3bt-0.1.0.dist-info/entry_points.txt +2 -0
- prosperity3bt-0.1.0.dist-info/top_level.txt +1 -0
prosperity3bt/runner.py
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
import os
|
2
|
+
from contextlib import closing, redirect_stdout
|
3
|
+
from io import StringIO
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from IPython.utils.io import Tee
|
7
|
+
from tqdm import tqdm
|
8
|
+
|
9
|
+
from prosperity3bt.data import LIMITS, BacktestData, read_day_data
|
10
|
+
from prosperity3bt.datamodel import Observation, Order, OrderDepth, Symbol, Trade, TradingState
|
11
|
+
from prosperity3bt.file_reader import FileReader
|
12
|
+
from prosperity3bt.models import ActivityLogRow, BacktestResult, MarketTrade, SandboxLogRow, TradeRow
|
13
|
+
|
14
|
+
|
15
|
+
def prepare_state(state: TradingState, data: BacktestData) -> None:
|
16
|
+
for product in data.products:
|
17
|
+
order_depth = OrderDepth()
|
18
|
+
row = data.prices[state.timestamp][product]
|
19
|
+
|
20
|
+
for price, volume in zip(row.bid_prices, row.bid_volumes):
|
21
|
+
order_depth.buy_orders[price] = volume
|
22
|
+
|
23
|
+
for price, volume in zip(row.ask_prices, row.ask_volumes):
|
24
|
+
order_depth.sell_orders[price] = -volume
|
25
|
+
|
26
|
+
state.order_depths[product] = order_depth
|
27
|
+
|
28
|
+
state.listings[product] = {
|
29
|
+
"symbol": product,
|
30
|
+
"product": product,
|
31
|
+
"denomination": 1,
|
32
|
+
}
|
33
|
+
|
34
|
+
|
35
|
+
def create_activity_logs(
|
36
|
+
state: TradingState,
|
37
|
+
data: BacktestData,
|
38
|
+
result: BacktestResult,
|
39
|
+
) -> None:
|
40
|
+
for product in data.products:
|
41
|
+
row = data.prices[state.timestamp][product]
|
42
|
+
|
43
|
+
product_profit_loss = data.profit_loss[product]
|
44
|
+
|
45
|
+
position = state.position.get(product, 0)
|
46
|
+
if position != 0:
|
47
|
+
product_profit_loss += position * row.mid_price
|
48
|
+
|
49
|
+
bid_prices_len = len(row.bid_prices)
|
50
|
+
bid_volumes_len = len(row.bid_volumes)
|
51
|
+
ask_prices_len = len(row.ask_prices)
|
52
|
+
ask_volumes_len = len(row.ask_volumes)
|
53
|
+
|
54
|
+
columns = [
|
55
|
+
result.day_num,
|
56
|
+
state.timestamp,
|
57
|
+
product,
|
58
|
+
row.bid_prices[0] if bid_prices_len > 0 else "",
|
59
|
+
row.bid_volumes[0] if bid_volumes_len > 0 else "",
|
60
|
+
row.bid_prices[1] if bid_prices_len > 1 else "",
|
61
|
+
row.bid_volumes[1] if bid_volumes_len > 1 else "",
|
62
|
+
row.bid_prices[2] if bid_prices_len > 2 else "",
|
63
|
+
row.bid_volumes[2] if bid_volumes_len > 2 else "",
|
64
|
+
row.ask_prices[0] if ask_prices_len > 0 else "",
|
65
|
+
row.ask_volumes[0] if ask_volumes_len > 0 else "",
|
66
|
+
row.ask_prices[1] if ask_prices_len > 1 else "",
|
67
|
+
row.ask_volumes[1] if ask_volumes_len > 1 else "",
|
68
|
+
row.ask_prices[2] if ask_prices_len > 2 else "",
|
69
|
+
row.ask_volumes[2] if ask_volumes_len > 2 else "",
|
70
|
+
row.mid_price,
|
71
|
+
product_profit_loss,
|
72
|
+
]
|
73
|
+
|
74
|
+
result.activity_logs.append(ActivityLogRow(columns))
|
75
|
+
|
76
|
+
|
77
|
+
def enforce_limits(
|
78
|
+
state: TradingState,
|
79
|
+
data: BacktestData,
|
80
|
+
orders: dict[Symbol, list[Order]],
|
81
|
+
sandbox_row: SandboxLogRow,
|
82
|
+
) -> None:
|
83
|
+
sandbox_log_lines = []
|
84
|
+
for product in data.products:
|
85
|
+
product_orders = orders.get(product, [])
|
86
|
+
product_position = state.position.get(product, 0)
|
87
|
+
|
88
|
+
total_long = sum(order.quantity for order in product_orders if order.quantity > 0)
|
89
|
+
total_short = sum(abs(order.quantity) for order in product_orders if order.quantity < 0)
|
90
|
+
|
91
|
+
if product_position + total_long > LIMITS[product] or product_position - total_short < -LIMITS[product]:
|
92
|
+
sandbox_log_lines.append(f"Orders for product {product} exceeded limit of {LIMITS[product]} set")
|
93
|
+
orders.pop(product)
|
94
|
+
|
95
|
+
if len(sandbox_log_lines) > 0:
|
96
|
+
sandbox_row.sandbox_log += "\n" + "\n".join(sandbox_log_lines)
|
97
|
+
|
98
|
+
|
99
|
+
def match_buy_order(
|
100
|
+
state: TradingState, data: BacktestData, order: Order, market_trades: list[MarketTrade]
|
101
|
+
) -> list[Trade]:
|
102
|
+
trades = []
|
103
|
+
|
104
|
+
order_depth = state.order_depths[order.symbol]
|
105
|
+
price_matches = sorted(price for price in order_depth.sell_orders.keys() if price <= order.price)
|
106
|
+
for price in price_matches:
|
107
|
+
volume = min(order.quantity, abs(order_depth.sell_orders[price]))
|
108
|
+
|
109
|
+
trades.append(Trade(order.symbol, price, volume, "SUBMISSION", "", state.timestamp))
|
110
|
+
|
111
|
+
state.position[order.symbol] = state.position.get(order.symbol, 0) + volume
|
112
|
+
data.profit_loss[order.symbol] -= price * volume
|
113
|
+
|
114
|
+
order_depth.sell_orders[price] += volume
|
115
|
+
if order_depth.sell_orders[price] == 0:
|
116
|
+
order_depth.sell_orders.pop(price)
|
117
|
+
|
118
|
+
order.quantity -= volume
|
119
|
+
if order.quantity == 0:
|
120
|
+
return trades
|
121
|
+
|
122
|
+
for market_trade in market_trades:
|
123
|
+
if market_trade.sell_quantity == 0 or market_trade.trade.price > order.price:
|
124
|
+
continue
|
125
|
+
|
126
|
+
volume = min(order.quantity, market_trade.sell_quantity)
|
127
|
+
|
128
|
+
trades.append(
|
129
|
+
Trade(order.symbol, order.price, volume, "SUBMISSION", market_trade.trade.seller, state.timestamp)
|
130
|
+
)
|
131
|
+
|
132
|
+
state.position[order.symbol] = state.position.get(order.symbol, 0) + volume
|
133
|
+
data.profit_loss[order.symbol] -= order.price * volume
|
134
|
+
|
135
|
+
market_trade.sell_quantity -= volume
|
136
|
+
|
137
|
+
order.quantity -= volume
|
138
|
+
if order.quantity == 0:
|
139
|
+
return trades
|
140
|
+
|
141
|
+
return trades
|
142
|
+
|
143
|
+
|
144
|
+
def match_sell_order(
|
145
|
+
state: TradingState, data: BacktestData, order: Order, market_trades: list[MarketTrade]
|
146
|
+
) -> list[Trade]:
|
147
|
+
trades = []
|
148
|
+
|
149
|
+
order_depth = state.order_depths[order.symbol]
|
150
|
+
price_matches = sorted((price for price in order_depth.buy_orders.keys() if price >= order.price), reverse=True)
|
151
|
+
for price in price_matches:
|
152
|
+
volume = min(abs(order.quantity), order_depth.buy_orders[price])
|
153
|
+
|
154
|
+
trades.append(Trade(order.symbol, price, volume, "", "SUBMISSION", state.timestamp))
|
155
|
+
|
156
|
+
state.position[order.symbol] = state.position.get(order.symbol, 0) - volume
|
157
|
+
data.profit_loss[order.symbol] += price * volume
|
158
|
+
|
159
|
+
order_depth.buy_orders[price] -= volume
|
160
|
+
if order_depth.buy_orders[price] == 0:
|
161
|
+
order_depth.buy_orders.pop(price)
|
162
|
+
|
163
|
+
order.quantity += volume
|
164
|
+
if order.quantity == 0:
|
165
|
+
return trades
|
166
|
+
|
167
|
+
for market_trade in market_trades:
|
168
|
+
if market_trade.buy_quantity == 0 or market_trade.trade.price < order.price:
|
169
|
+
continue
|
170
|
+
|
171
|
+
volume = min(abs(order.quantity), market_trade.buy_quantity)
|
172
|
+
|
173
|
+
trades.append(Trade(order.symbol, order.price, volume, market_trade.trade.buyer, "SUBMISSION", state.timestamp))
|
174
|
+
|
175
|
+
state.position[order.symbol] = state.position.get(order.symbol, 0) - volume
|
176
|
+
data.profit_loss[order.symbol] += order.price * volume
|
177
|
+
|
178
|
+
market_trade.buy_quantity -= volume
|
179
|
+
|
180
|
+
order.quantity += volume
|
181
|
+
if order.quantity == 0:
|
182
|
+
return trades
|
183
|
+
|
184
|
+
return trades
|
185
|
+
|
186
|
+
|
187
|
+
def match_order(state: TradingState, data: BacktestData, order: Order, market_trades: list[MarketTrade]) -> list[Trade]:
|
188
|
+
if order.quantity > 0:
|
189
|
+
return match_buy_order(state, data, order, market_trades)
|
190
|
+
elif order.quantity < 0:
|
191
|
+
return match_sell_order(state, data, order, market_trades)
|
192
|
+
else:
|
193
|
+
return []
|
194
|
+
|
195
|
+
|
196
|
+
def match_orders(
|
197
|
+
state: TradingState,
|
198
|
+
data: BacktestData,
|
199
|
+
orders: dict[Symbol, list[Order]],
|
200
|
+
result: BacktestResult,
|
201
|
+
disable_trades_matching: bool,
|
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]
|
206
|
+
|
207
|
+
for product in data.products:
|
208
|
+
new_trades = []
|
209
|
+
|
210
|
+
for order in orders.get(product, []):
|
211
|
+
new_trades.extend(
|
212
|
+
match_order(
|
213
|
+
state,
|
214
|
+
data,
|
215
|
+
order,
|
216
|
+
[] if disable_trades_matching else market_trades.get(product, []),
|
217
|
+
)
|
218
|
+
)
|
219
|
+
|
220
|
+
if len(new_trades) > 0:
|
221
|
+
state.own_trades[product] = new_trades
|
222
|
+
result.trades.extend([TradeRow(trade) for trade in new_trades])
|
223
|
+
|
224
|
+
for product, trades in market_trades.items():
|
225
|
+
for trade in trades:
|
226
|
+
trade.trade.quantity = min(trade.buy_quantity, trade.sell_quantity)
|
227
|
+
|
228
|
+
remaining_market_trades = [t.trade for t in trades if t.trade.quantity > 0]
|
229
|
+
|
230
|
+
state.market_trades[product] = remaining_market_trades
|
231
|
+
result.trades.extend([TradeRow(trade) for trade in remaining_market_trades])
|
232
|
+
|
233
|
+
|
234
|
+
def run_backtest(
|
235
|
+
trader: Any,
|
236
|
+
file_reader: FileReader,
|
237
|
+
round_num: int,
|
238
|
+
day_num: int,
|
239
|
+
print_output: bool,
|
240
|
+
disable_trades_matching: bool,
|
241
|
+
no_names: bool,
|
242
|
+
show_progress_bar: bool,
|
243
|
+
) -> BacktestResult:
|
244
|
+
data = read_day_data(file_reader, round_num, day_num, no_names)
|
245
|
+
|
246
|
+
os.environ["PROSPERITY3BT_ROUND"] = str(round_num)
|
247
|
+
os.environ["PROSPERITY3BT_DAY"] = str(day_num)
|
248
|
+
|
249
|
+
trader_data = ""
|
250
|
+
state = TradingState(
|
251
|
+
traderData=trader_data,
|
252
|
+
timestamp=0,
|
253
|
+
listings={},
|
254
|
+
order_depths={},
|
255
|
+
own_trades={},
|
256
|
+
market_trades={},
|
257
|
+
position={},
|
258
|
+
observations=Observation({}, {}),
|
259
|
+
)
|
260
|
+
|
261
|
+
result = BacktestResult(
|
262
|
+
round_num=data.round_num,
|
263
|
+
day_num=data.day_num,
|
264
|
+
sandbox_logs=[],
|
265
|
+
activity_logs=[],
|
266
|
+
trades=[],
|
267
|
+
)
|
268
|
+
|
269
|
+
timestamps = sorted(data.prices.keys())
|
270
|
+
timestamps_iterator = tqdm(timestamps, ascii=True) if show_progress_bar else timestamps
|
271
|
+
|
272
|
+
for timestamp in timestamps_iterator:
|
273
|
+
state.timestamp = timestamp
|
274
|
+
state.traderData = trader_data
|
275
|
+
|
276
|
+
prepare_state(state, data)
|
277
|
+
|
278
|
+
stdout = StringIO()
|
279
|
+
|
280
|
+
# Tee calls stdout.close(), making stdout.getvalue() impossible
|
281
|
+
# This override makes getvalue() possible after close()
|
282
|
+
stdout.close = lambda: None
|
283
|
+
|
284
|
+
if print_output:
|
285
|
+
with closing(Tee(stdout)):
|
286
|
+
orders, conversions, trader_data = trader.run(state)
|
287
|
+
else:
|
288
|
+
with redirect_stdout(stdout):
|
289
|
+
orders, conversions, trader_data = trader.run(state)
|
290
|
+
|
291
|
+
sandbox_row = SandboxLogRow(
|
292
|
+
timestamp=timestamp,
|
293
|
+
sandbox_log="",
|
294
|
+
lambda_log=stdout.getvalue().rstrip(),
|
295
|
+
)
|
296
|
+
|
297
|
+
result.sandbox_logs.append(sandbox_row)
|
298
|
+
|
299
|
+
create_activity_logs(state, data, result)
|
300
|
+
enforce_limits(state, data, orders, sandbox_row)
|
301
|
+
match_orders(state, data, orders, result, disable_trades_matching)
|
302
|
+
|
303
|
+
return result
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Jasper van Merle
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1,129 @@
|
|
1
|
+
Metadata-Version: 2.2
|
2
|
+
Name: prosperity3bt
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Backtester for IMC Prosperity 3 algorithms
|
5
|
+
Author-email: Jasper van Merle <jaspervmerle@gmail.com>
|
6
|
+
License: MIT License
|
7
|
+
|
8
|
+
Copyright (c) 2024 Jasper van Merle
|
9
|
+
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
12
|
+
in the Software without restriction, including without limitation the rights
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
15
|
+
furnished to do so, subject to the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
18
|
+
copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
26
|
+
SOFTWARE.
|
27
|
+
|
28
|
+
Project-URL: Repository, https://github.com/jmerle/imc-prosperity-3-backtester
|
29
|
+
Project-URL: Issues, https://github.com/jmerle/imc-prosperity-3-backtester/issues
|
30
|
+
Project-URL: Changelog, https://github.com/jmerle/imc-prosperity-3-backtester/releases
|
31
|
+
Keywords: imc,prosperity,backtest,backtester
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
33
|
+
Classifier: Intended Audience :: Developers
|
34
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
37
|
+
Requires-Python: >=3.9
|
38
|
+
Description-Content-Type: text/markdown
|
39
|
+
License-File: LICENSE
|
40
|
+
Requires-Dist: ipython
|
41
|
+
Requires-Dist: jsonpickle
|
42
|
+
Requires-Dist: orjson
|
43
|
+
Requires-Dist: tqdm
|
44
|
+
|
45
|
+
# IMC Prosperity 3 Backtester
|
46
|
+
|
47
|
+
[](https://github.com/jmerle/imc-prosperity-3-backtester/actions/workflows/build.yml)
|
48
|
+
[](https://pypi.org/project/prosperity3bt/)
|
49
|
+
|
50
|
+
This repository contains a backtester [IMC Prosperity 3](https://prosperity.imc.com/) algorithms, based on [my backtester for Prosperity 2](https://github.com/jmerle/imc-prosperity-2-backtester). The output it generates closely matches the format of the output generated by the official submission environment and is therefore compatible with my [Prosperity 3 Visualizer](https://github.com/jmerle/imc-prosperity-3-visualizer) (assuming your code contains the visualizer's required prerequisites as explained on the visualizer's homepage).
|
51
|
+
|
52
|
+
## Usage
|
53
|
+
|
54
|
+
Basic usage:
|
55
|
+
```sh
|
56
|
+
# Install the latest version of the backtester
|
57
|
+
$ pip install -U prosperity3bt
|
58
|
+
|
59
|
+
# Run the backtester on an algorithm using all data from round 0
|
60
|
+
$ prosperity3bt <path to algorithm file> 0
|
61
|
+
```
|
62
|
+
|
63
|
+
Run `pip install -U prosperity3bt` again when you want to update the backtester to the latest version.
|
64
|
+
|
65
|
+
Some more usage examples:
|
66
|
+
```sh
|
67
|
+
# Backtest on all days from round 1
|
68
|
+
$ prosperity3bt example/starter.py 1
|
69
|
+
|
70
|
+
# Backtest on round 1 day 0
|
71
|
+
$ prosperity3bt example/starter.py 1-0
|
72
|
+
|
73
|
+
# Backtest on round 1 day -1 and round 1 day 0
|
74
|
+
$ prosperity3bt example/starter.py 1--1 1-0
|
75
|
+
|
76
|
+
# Backtest on all days from rounds 1 and 2
|
77
|
+
$ prosperity3bt example/starter.py 1 2
|
78
|
+
|
79
|
+
# You get the idea
|
80
|
+
|
81
|
+
# Merge profit and loss across days
|
82
|
+
$ prosperity3bt example/starter.py 1 --merge-pnl
|
83
|
+
|
84
|
+
# Automatically open the result in the visualizer when done
|
85
|
+
# Assumes your algorithm logs in the visualizer's expected format
|
86
|
+
$ prosperity3bt example/starter.py 1 --vis
|
87
|
+
|
88
|
+
# Write algorithm output to custom file
|
89
|
+
$ prosperity3bt example/starter.py 1 --out example.log
|
90
|
+
|
91
|
+
# Skip saving the output log to a file
|
92
|
+
$ prosperity3bt example/starter.py 1 --no-out
|
93
|
+
|
94
|
+
# Backtest on custom data
|
95
|
+
# Requires the value passed to `--data` to be a path to a directory that is similar in structure to https://github.com/jmerle/imc-prosperity-3-backtester/tree/master/prosperity3bt/resources
|
96
|
+
$ prosperity3bt example/starter.py 1 --data prosperity3bt/resources
|
97
|
+
|
98
|
+
# Print trader's output to stdout while running
|
99
|
+
# This may be helpful when debugging a broken trader
|
100
|
+
$ 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
|
+
```
|
105
|
+
|
106
|
+
## Order Matching
|
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).
|
109
|
+
|
110
|
+
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
|
+
|
112
|
+
## Data Files
|
113
|
+
|
114
|
+
Data for the following rounds is included:
|
115
|
+
- Round 0: prices and anonymized trades data on RAINFOREST_RESIN and KELP that was used during tutorial submission runs.
|
116
|
+
|
117
|
+
## Environment Variables
|
118
|
+
|
119
|
+
During backtests two environment variables are set for the trader to know the round and day it's being backtested on. The environment variable named `PROSPERITY3BT_ROUND` contains the round number and `PROSPERITY3BT_DAY` contains the day number. Note that these environment variables do not exist in the official submission environment, so make sure the code you submit doesn't require them to be defined.
|
120
|
+
|
121
|
+
## Development
|
122
|
+
|
123
|
+
Follow these steps if you want to make changes to the backtester:
|
124
|
+
1. Install [uv](https://docs.astral.sh/uv/).
|
125
|
+
2. Clone (or fork and clone) this repository.
|
126
|
+
3. Open a terminal in your clone of the repository.
|
127
|
+
4. Create a venv with `uv venv` and activate it.
|
128
|
+
5. Run `uv sync`.
|
129
|
+
6. Any changes you make are now automatically taken into account the next time you run `prosperity3bt` inside the venv.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
prosperity3bt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
prosperity3bt/__main__.py,sha256=eEVQNwXEB2U6zzhOCcxXbtRlFJhspdaxn9637PcghDI,10523
|
3
|
+
prosperity3bt/data.py,sha256=zinLPzo13cB7f-0K0ud9Uw8qMGHFsiVs8JsmlqA4NP4,3872
|
4
|
+
prosperity3bt/datamodel.py,sha256=jfU_bIlIRSJCjfXbvb2bVujQ6SzHMYqUMFP5zvsyH3A,4044
|
5
|
+
prosperity3bt/file_reader.py,sha256=KOqedBdzbLax9u2GIOYjfp3EVVVC00RWZrjWbaLe-bc,1392
|
6
|
+
prosperity3bt/models.py,sha256=WhqW8aFzWmJrhTQNAI1JcW2xUDJ_ZPYgxanyrFMZUMA,2446
|
7
|
+
prosperity3bt/runner.py,sha256=fNSnacez-CjSKJ0gHnbOPjn6sGtHcGO8NBIckB_vroQ,10267
|
8
|
+
prosperity3bt/resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
9
|
+
prosperity3bt/resources/round0/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
prosperity3bt/resources/round0/prices_round_0_day_-2.csv,sha256=5g-A0Fa3CgrqCKpOXvh4WM8Y3UqyRCr8dRJVYvtL7lo,249937
|
11
|
+
prosperity3bt/resources/round0/trades_round_0_day_-2_nn.csv,sha256=Hh8tpscl4QePzOEDrMIdFXTdaK4ok745eImnJvHUxuw,39258
|
12
|
+
prosperity3bt-0.1.0.dist-info/LICENSE,sha256=fel8BL5f1w-o-aUb4ZDJchOOx_ldqM0V9aIxl5G00MA,1073
|
13
|
+
prosperity3bt-0.1.0.dist-info/METADATA,sha256=xI4rOuQjBkH5J2Nr7MFiWr31Q5bhrCxdjh3OHtb496c,6770
|
14
|
+
prosperity3bt-0.1.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
15
|
+
prosperity3bt-0.1.0.dist-info/entry_points.txt,sha256=09PWTfMVNppmPEDXrbOtIRK087zoC5W2IBXWpD7WolQ,62
|
16
|
+
prosperity3bt-0.1.0.dist-info/top_level.txt,sha256=aPf2VrYQr3L02jA7JNT0lEFbIG0-T9f0TLwHdxgVJIc,14
|
17
|
+
prosperity3bt-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
prosperity3bt
|