-
Notifications
You must be signed in to change notification settings - Fork 109
Expand file tree
/
Copy pathtest_backtesting_fees.py
More file actions
175 lines (148 loc) · 6.77 KB
/
Copy pathtest_backtesting_fees.py
File metadata and controls
175 lines (148 loc) · 6.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# Basana
#
# Copyright 2022 Gabriel Martin Becedillas Ruiz
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from decimal import Decimal
from basana.backtesting import fees, orders
from basana.backtesting.exchange import Fill, OrderOperation
from basana.core import dt
from .common import btc_pair, btc_pair_info
def test_percentage_fee_with_partial_fills():
fee_strategy = fees.Percentage(Decimal("1"))
order = orders.MarketOrder("1", OrderOperation.BUY, btc_pair, btc_pair_info, Decimal("0.01"))
# Fill #1 - A 0.009 fee gets rounded to 0.01
balance_updates = {
"BTC": Decimal("0.001"),
"USD": Decimal("-0.9"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"USD": Decimal("-0.009")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"USD": Decimal("-0.01")}, Decimal(9))
)
# Fill #2 - A 0.008 fee gets rounded to 0.01
balance_updates = {
"BTC": Decimal("0.001"),
"USD": Decimal("-0.9"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"USD": Decimal("-0.008")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"USD": Decimal("-0.01")}, Decimal(9))
)
# Fill #3 - Final fill. Total fees, prior to rounding, should be 0.118, but we charged 0.02 already, so the last
# chunk, prior to rounding, should be 0.098.
balance_updates = {
"BTC": Decimal("0.008"),
"USD": Decimal("-10"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"USD": Decimal("-0.098")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"USD": Decimal("-0.1")}, Decimal(9))
)
def test_percentage_fee_with_minium():
fee_strategy = fees.Percentage(Decimal("1"), min_fee=Decimal("5"))
order = orders.MarketOrder("1", OrderOperation.BUY, btc_pair, btc_pair_info, Decimal("0.1"))
balance_updates = {
"BTC": Decimal("0.1"),
"USD": Decimal("-50.15"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"USD": Decimal("-5")}
def test_percentage_fee_base_currency_with_partial_fills():
fee_strategy = fees.Percentage(Decimal("1"), fee_currency=fees.FeeCurrency.BASE)
order = orders.MarketOrder("1", OrderOperation.BUY, btc_pair, btc_pair_info, Decimal("0.01"))
# Fill #1 - base amount: 0.001 BTC, fee = 0.001 * 1% = 0.00001 BTC
balance_updates = {
"BTC": Decimal("0.001"),
"USD": Decimal("-0.9"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"BTC": Decimal("-0.00001")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"BTC": Decimal("-0.00001")}, Decimal(900))
)
# Fill #2 - cumulative base = 0.002, total fee = 0.00002, already charged 0.00001, pending = 0.00001
balance_updates = {
"BTC": Decimal("0.001"),
"USD": Decimal("-0.9"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"BTC": Decimal("-0.00001")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"BTC": Decimal("-0.00001")}, Decimal(900))
)
# Fill #3 - cumulative base = 0.01, total fee = 0.0001, already charged 0.00002, pending = 0.00008
balance_updates = {
"BTC": Decimal("0.008"),
"USD": Decimal("-7.2"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"BTC": Decimal("-0.00008")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"BTC": Decimal("-0.00008")}, Decimal(900))
)
def test_percentage_fee_base_currency_with_partial_fills_for_sell():
fee_strategy = fees.Percentage(Decimal("1"), fee_currency=fees.FeeCurrency.BASE)
order = orders.MarketOrder("1", OrderOperation.SELL, btc_pair, btc_pair_info, Decimal("0.01"))
# Fill #1 - base amount: 0.001 BTC, fee = 0.001 * 1% = 0.00001 BTC
balance_updates = {
"BTC": Decimal("-0.001"),
"USD": Decimal("0.9"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"BTC": Decimal("-0.00001")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"BTC": Decimal("-0.00001")}, Decimal(900))
)
# Fill #2 - cumulative base = -0.002, total fee = 0.00002, already charged 0.00001, pending = 0.00001
balance_updates = {
"BTC": Decimal("-0.001"),
"USD": Decimal("0.9"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"BTC": Decimal("-0.00001")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"BTC": Decimal("-0.00001")}, Decimal(900))
)
# Fill #3 - cumulative base = -0.01, total fee = 0.00010, already charged 0.00002, pending = 0.00008
balance_updates = {
"BTC": Decimal("-0.008"),
"USD": Decimal("7.2"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"BTC": Decimal("-0.00008")}
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"BTC": Decimal("-0.00008")}, Decimal(900))
)
def test_percentage_fee_no_pending_fee():
# Test that when charged_fee already covers the total, pending_fee >= 0 and no additional fee is returned.
fee_strategy = fees.Percentage(Decimal("1"))
order = orders.MarketOrder("1", OrderOperation.BUY, btc_pair, btc_pair_info, Decimal("0.01"))
# Fill #1: charge a higher fee than 1% (simulating rounding overcharge).
balance_updates = {
"BTC": Decimal("0.001"),
"USD": Decimal("-0.9"),
}
# Manually overcharge by setting charged_fee to more than the 1% of total.
order.add_fill(
Fill(dt.utc_now(), balance_updates, {"USD": Decimal("-0.10")}, Decimal(9))
)
# Fill #2: the total fee is less than already charged, so pending_fee >= 0 (no extra fee returned).
balance_updates2 = {
"BTC": Decimal("0.001"),
"USD": Decimal("-0.9"),
}
result = fee_strategy.calculate_fees(order, balance_updates2)
assert result == {}
def test_percentage_fee_base_currency_with_minimum():
fee_strategy = fees.Percentage(Decimal("1"), min_fee=Decimal("0.001"), fee_currency=fees.FeeCurrency.BASE)
order = orders.MarketOrder("1", OrderOperation.BUY, btc_pair, btc_pair_info, Decimal("0.05"))
# 0.05 BTC * 1% = 0.0005 BTC fee, but min_fee=0.001, so fee = 0.001 BTC
balance_updates = {
"BTC": Decimal("0.05"),
"USD": Decimal("-2500"),
}
assert fee_strategy.calculate_fees(order, balance_updates) == {"BTC": Decimal("-0.001")}