Skip to content

Commit adc85fb

Browse files
committed
#152 : Simulate fees in Try Mode adapter
1 parent 80ea3aa commit adc85fb

File tree

4 files changed

+68
-82
lines changed

4 files changed

+68
-82
lines changed

README.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,10 +214,7 @@ The [`TryModeExchangeAdapter`](./bxbot-exchanges/src/main/java/com/gazbert/bxbot
214214
configured by default to delegate public API calls to the
215215
[`BitstampExchangeAdapter`](./bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/BitstampExchangeAdapter.java).
216216
It simulates the private API (order management) calls; it's good for testing your initial setup and
217-
paper trading, but you'll eventually want to send live orders to the exchange! You'll still need to
218-
configure your API credentials for Bitstamp because the adapter makes an authenticated API call to
219-
get the buy/sell fees from the [/balance](https://www.bitstamp.net/api/#account-balance) endpoint.
220-
217+
``paper trading, but you'll eventually want to send live orders to the exchange!
221218

222219
The configuration below shows how to live-trade with Bitstamp:
223220

bxbot-exchanges/src/main/java/com/gazbert/bxbot/exchanges/TryModeExchangeAdapter.java

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,32 @@ public class TryModeExchangeAdapter extends AbstractExchangeAdapter implements E
6868
private static final Logger LOG = LogManager.getLogger();
6969

7070
private static final String SIMULATED_COUNTER_CURRENCY_PROPERTY_NAME = "simulatedCounterCurrency";
71-
private static final String COUNTER_CURRENCY_START_BALANCE_PROPERTY_NAME =
72-
"counterCurrencyStartingBalance";
71+
private static final String SIMULATED_COUNTER_CURRENCY_START_BALANCE_PROPERTY_NAME =
72+
"simulatedCounterCurrencyStartingBalance";
73+
7374
private static final String SIMULATED_BASE_CURRENCY_PROPERTY_NAME = "simulatedBaseCurrency";
74-
private static final String BASE_CURRENCY_START_BALANCE_PROPERTY_NAME =
75-
"baseCurrencyStartingBalance";
75+
private static final String SIMULATED_BASE_CURRENCY_START_BALANCE_PROPERTY_NAME =
76+
"simulatedBaseCurrencyStartingBalance";
77+
78+
private static final String SIMULATED_SELL_FEE_PROPERTY_NAME = "simulatedSellFee";
79+
private static final String SIMULATED_BUY_FEE_PROPERTY_NAME = "simulatedBuyFee";
80+
7681
private static final String DELEGATE_ADAPTER_CLASS_PROPERTY_NAME = "delegateAdapter";
7782

7883
private String simulatedBaseCurrency;
84+
private BigDecimal simulatedBaseCurrencyBalance;
85+
7986
private String simulatedCounterCurrency;
80-
private BigDecimal counterCurrencyBalance;
87+
private BigDecimal simulatedCounterCurrencyBalance;
88+
89+
private BigDecimal simulatedSellFee;
90+
private BigDecimal simulatedBuyFee;
91+
8192
private String delegateExchangeClassName;
8293

8394
private ExchangeAdapter delegateExchangeAdapter;
8495

8596
private OpenOrder currentOpenOrder;
86-
private BigDecimal baseCurrencyBalance;
8797
private boolean isOpenOrderCheckReentering;
8898

8999
@Override
@@ -173,31 +183,21 @@ public BigDecimal getLatestMarketPrice(String marketId)
173183
@Override
174184
public BalanceInfo getBalanceInfo() {
175185
final HashMap<String, BigDecimal> availableBalances = new HashMap<>();
176-
availableBalances.put(simulatedBaseCurrency, baseCurrencyBalance);
177-
availableBalances.put(simulatedCounterCurrency, counterCurrencyBalance);
186+
availableBalances.put(simulatedBaseCurrency, simulatedBaseCurrencyBalance);
187+
availableBalances.put(simulatedCounterCurrency, simulatedCounterCurrencyBalance);
178188
final BalanceInfo currentBalance = new BalanceInfoImpl(availableBalances, new HashMap<>());
179189
LOG.info(() -> "Return the following simulated balances: " + currentBalance);
180190
return currentBalance;
181191
}
182192

183193
@Override
184-
public BigDecimal getPercentageOfBuyOrderTakenForExchangeFee(String marketId)
185-
throws TradingApiException, ExchangeNetworkException {
186-
LOG.info(
187-
() ->
188-
"Delegate 'getPercentageOfBuyOrderTakenForExchangeFee'"
189-
+ "to the configured delegation exchange adapter.");
190-
return delegateExchangeAdapter.getPercentageOfBuyOrderTakenForExchangeFee(marketId);
194+
public BigDecimal getPercentageOfBuyOrderTakenForExchangeFee(String marketId) {
195+
return simulatedBuyFee;
191196
}
192197

193198
@Override
194-
public BigDecimal getPercentageOfSellOrderTakenForExchangeFee(String marketId)
195-
throws TradingApiException, ExchangeNetworkException {
196-
LOG.info(
197-
() ->
198-
"Delegate 'getPercentageOfSellOrderTakenForExchangeFee'"
199-
+ "to the configured delegation exchange adapter.");
200-
return delegateExchangeAdapter.getPercentageOfSellOrderTakenForExchangeFee(marketId);
199+
public BigDecimal getPercentageOfSellOrderTakenForExchangeFee(String marketId) {
200+
return simulatedSellFee;
201201
}
202202

203203
@Override
@@ -215,24 +215,33 @@ private void setOtherConfig(ExchangeConfig exchangeConfig) {
215215
LOG.info(() -> "Base currency to be simulated:" + simulatedBaseCurrency);
216216

217217
final String startingBaseBalanceInConfig =
218-
getOtherConfigItem(otherConfig, BASE_CURRENCY_START_BALANCE_PROPERTY_NAME);
219-
baseCurrencyBalance = new BigDecimal(startingBaseBalanceInConfig);
218+
getOtherConfigItem(otherConfig, SIMULATED_BASE_CURRENCY_START_BALANCE_PROPERTY_NAME);
219+
simulatedBaseCurrencyBalance = new BigDecimal(startingBaseBalanceInConfig);
220220
LOG.info(
221221
() ->
222222
"Base currency balance at simulation start in BigDecimal format: "
223-
+ baseCurrencyBalance);
223+
+ simulatedBaseCurrencyBalance);
224224

225225
simulatedCounterCurrency =
226226
getOtherConfigItem(otherConfig, SIMULATED_COUNTER_CURRENCY_PROPERTY_NAME);
227227
LOG.info(() -> "Counter currency to be simulated:" + simulatedCounterCurrency);
228228

229229
final String startingBalanceInConfig =
230-
getOtherConfigItem(otherConfig, COUNTER_CURRENCY_START_BALANCE_PROPERTY_NAME);
231-
counterCurrencyBalance = new BigDecimal(startingBalanceInConfig);
230+
getOtherConfigItem(otherConfig, SIMULATED_COUNTER_CURRENCY_START_BALANCE_PROPERTY_NAME);
231+
simulatedCounterCurrencyBalance = new BigDecimal(startingBalanceInConfig);
232232
LOG.info(
233233
() ->
234234
"Counter currency balance at simulation start in BigDecimal format: "
235-
+ counterCurrencyBalance);
235+
+ simulatedCounterCurrencyBalance);
236+
237+
final String sellFeeInConfig =
238+
getOtherConfigItem(otherConfig, SIMULATED_SELL_FEE_PROPERTY_NAME);
239+
simulatedSellFee = new BigDecimal(sellFeeInConfig);
240+
LOG.info(() -> "Sell Fee at simulation start in BigDecimal format: " + simulatedSellFee);
241+
242+
final String buyFeeInConfig = getOtherConfigItem(otherConfig, SIMULATED_BUY_FEE_PROPERTY_NAME);
243+
simulatedBuyFee = new BigDecimal(buyFeeInConfig);
244+
LOG.info(() -> "Buy Fee at simulation start in BigDecimal format: " + simulatedBuyFee);
236245

237246
delegateExchangeClassName =
238247
getOtherConfigItem(otherConfig, DELEGATE_ADAPTER_CLASS_PROPERTY_NAME);
@@ -250,6 +259,7 @@ private void initializeAdapterDelegation(ExchangeConfig config) {
250259
delegateExchangeAdapter.init(config);
251260
}
252261

262+
@SuppressWarnings("unchecked")
253263
private ExchangeAdapter createDelegateExchangeAdapter() {
254264
LOG.info(() -> "Creating the delegate exchange adapter '" + delegateExchangeClassName + "'...");
255265
try {
@@ -308,8 +318,9 @@ private void checkOpenSellOrderExecution(String marketId)
308318
final BigDecimal buyFees =
309319
getPercentageOfSellOrderTakenForExchangeFee(marketId).multiply(orderPrice);
310320
final BigDecimal netOrderPrice = orderPrice.subtract(buyFees);
311-
counterCurrencyBalance = counterCurrencyBalance.add(netOrderPrice);
312-
baseCurrencyBalance = baseCurrencyBalance.subtract(currentOpenOrder.getOriginalQuantity());
321+
simulatedCounterCurrencyBalance = simulatedCounterCurrencyBalance.add(netOrderPrice);
322+
simulatedBaseCurrencyBalance =
323+
simulatedBaseCurrencyBalance.subtract(currentOpenOrder.getOriginalQuantity());
313324
currentOpenOrder = null;
314325
}
315326
}
@@ -326,8 +337,9 @@ private void checkOpenBuyOrderExecution(String marketId)
326337
final BigDecimal buyFees =
327338
getPercentageOfBuyOrderTakenForExchangeFee(marketId).multiply(orderPrice);
328339
final BigDecimal netOrderPrice = orderPrice.add(buyFees);
329-
counterCurrencyBalance = counterCurrencyBalance.subtract(netOrderPrice);
330-
baseCurrencyBalance = baseCurrencyBalance.add(currentOpenOrder.getOriginalQuantity());
340+
simulatedCounterCurrencyBalance = simulatedCounterCurrencyBalance.subtract(netOrderPrice);
341+
simulatedBaseCurrencyBalance =
342+
simulatedBaseCurrencyBalance.add(currentOpenOrder.getOriginalQuantity());
331343
currentOpenOrder = null;
332344
}
333345
}

bxbot-exchanges/src/test/java/com/gazbert/bxbot/exchanges/TestTryModeExchangeAdapter.java

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,6 @@ public class TestTryModeExchangeAdapter extends AbstractExchangeAdapter {
9393
private static final BigDecimal SELL_ORDER_QUANTITY = new BigDecimal("0.03");
9494
private static final String UNRECOGNISED_ORDER_ID = "80894263";
9595

96-
private static final BigDecimal PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE =
97-
new BigDecimal("0.0025");
98-
private static final BigDecimal PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE =
99-
new BigDecimal("0.0024");
100-
10196
private static final BigDecimal LAST = new BigDecimal("18789.58");
10297
private static final BigDecimal BID = new BigDecimal("18778.25");
10398
private static final BigDecimal ASK = new BigDecimal("18783.33");
@@ -160,6 +155,13 @@ public class TestTryModeExchangeAdapter extends AbstractExchangeAdapter {
160155
private static final String COUNTER_CURRENCY = "USD";
161156
private static final String COUNTER_CURRENCY_STARTING_BALANCE = "100.0";
162157

158+
private static final String SIMULATED_SELL_FEE = "0.2";
159+
private static final String SIMULATED_BUY_FEE = "0.1";
160+
private static final BigDecimal PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE =
161+
new BigDecimal(SIMULATED_SELL_FEE);
162+
private static final BigDecimal PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE =
163+
new BigDecimal(SIMULATED_BUY_FEE);
164+
163165
private static final String CLIENT_ID = "clientId123";
164166
private static final String KEY = "key123";
165167
private static final String SECRET = "notGonnaTellYa";
@@ -185,17 +187,20 @@ public void setupForEachTest() {
185187

186188
OtherConfig otherConfig = PowerMock.createMock(OtherConfig.class);
187189
expect(otherConfig.getItem("simulatedBaseCurrency")).andReturn(BASE_CURRENCY).atLeastOnce();
188-
expect(otherConfig.getItem("baseCurrencyStartingBalance"))
190+
expect(otherConfig.getItem("simulatedBaseCurrencyStartingBalance"))
189191
.andReturn(BASE_CURRENCY_STARTING_BALANCE)
190192
.atLeastOnce();
191193

192194
expect(otherConfig.getItem("simulatedCounterCurrency"))
193195
.andReturn(COUNTER_CURRENCY)
194196
.atLeastOnce();
195-
expect(otherConfig.getItem("counterCurrencyStartingBalance"))
197+
expect(otherConfig.getItem("simulatedCounterCurrencyStartingBalance"))
196198
.andReturn(COUNTER_CURRENCY_STARTING_BALANCE)
197199
.atLeastOnce();
198200

201+
expect(otherConfig.getItem("simulatedSellFee")).andReturn(SIMULATED_SELL_FEE).atLeastOnce();
202+
expect(otherConfig.getItem("simulatedBuyFee")).andReturn(SIMULATED_BUY_FEE).atLeastOnce();
203+
199204
expect(otherConfig.getItem("delegateAdapter")).andReturn(DELEGATE_ADAPTER).atLeastOnce();
200205

201206
authenticationConfig = PowerMock.createMock(AuthenticationConfig.class);
@@ -303,12 +308,6 @@ public void testCreateBuyOrderThatFillsInstantly() throws Exception {
303308
PowerMock.expectPrivate(delegateExchangeAdapter, MOCKED_GET_TICKER_METHOD, eq(MARKET_ID))
304309
.andReturn(tickerResponse);
305310

306-
PowerMock.expectPrivate(
307-
delegateExchangeAdapter,
308-
MOCKED_GET_PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE,
309-
eq(MARKET_ID))
310-
.andReturn(PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE);
311-
312311
final TryModeExchangeAdapter tryModeExchangeAdapter =
313312
PowerMock.createPartialMockAndInvokeDefaultConstructor(
314313
TryModeExchangeAdapter.class, MOCKED_CREATE_DELEGATE_EXCHANGE_ADAPTER);
@@ -375,12 +374,6 @@ public void testCreateSellOrderThatFillsInstantly() throws Exception {
375374
PowerMock.expectPrivate(delegateExchangeAdapter, MOCKED_GET_TICKER_METHOD, eq(MARKET_ID))
376375
.andReturn(tickerResponse);
377376

378-
PowerMock.expectPrivate(
379-
delegateExchangeAdapter,
380-
MOCKED_GET_PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE,
381-
eq(MARKET_ID))
382-
.andReturn(PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE);
383-
384377
final TryModeExchangeAdapter tryModeExchangeAdapter =
385378
PowerMock.createPartialMockAndInvokeDefaultConstructor(
386379
TryModeExchangeAdapter.class, MOCKED_CREATE_DELEGATE_EXCHANGE_ADAPTER);
@@ -609,12 +602,6 @@ public void testGettingYourOpenOrdersWhenSellOrderFilled() throws Exception {
609602
PowerMock.expectPrivate(delegateExchangeAdapter, MOCKED_GET_TICKER_METHOD, eq(MARKET_ID))
610603
.andReturn(tickerResponse);
611604

612-
PowerMock.expectPrivate(
613-
delegateExchangeAdapter,
614-
MOCKED_GET_PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE,
615-
eq(MARKET_ID))
616-
.andReturn(PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE);
617-
618605
final TryModeExchangeAdapter tryModeExchangeAdapter =
619606
PowerMock.createPartialMockAndInvokeDefaultConstructor(
620607
TryModeExchangeAdapter.class, MOCKED_CREATE_DELEGATE_EXCHANGE_ADAPTER);
@@ -714,12 +701,6 @@ public void testGettingYourOpenOrdersWhenBuyOrderFilled() throws Exception {
714701
PowerMock.expectPrivate(delegateExchangeAdapter, MOCKED_GET_TICKER_METHOD, eq(MARKET_ID))
715702
.andReturn(tickerResponse);
716703

717-
PowerMock.expectPrivate(
718-
delegateExchangeAdapter,
719-
MOCKED_GET_PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE,
720-
eq(MARKET_ID))
721-
.andReturn(PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE);
722-
723704
final TryModeExchangeAdapter tryModeExchangeAdapter =
724705
PowerMock.createPartialMockAndInvokeDefaultConstructor(
725706
TryModeExchangeAdapter.class, MOCKED_CREATE_DELEGATE_EXCHANGE_ADAPTER);
@@ -908,17 +889,12 @@ public void testGettingBalanceInfoSuccessfully() throws Exception {
908889

909890
@Test
910891
public void testGettingExchangeBuyingFeeSuccessfully() throws Exception {
892+
911893
final BitstampExchangeAdapter delegateExchangeAdapter =
912894
PowerMock.createPartialMockAndInvokeDefaultConstructor(
913895
BitstampExchangeAdapter.class,
914896
MOCKED_GET_PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE);
915897

916-
PowerMock.expectPrivate(
917-
delegateExchangeAdapter,
918-
MOCKED_GET_PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE,
919-
eq(MARKET_ID))
920-
.andReturn(PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE);
921-
922898
final TryModeExchangeAdapter tryModeExchangeAdapter =
923899
PowerMock.createPartialMockAndInvokeDefaultConstructor(
924900
TryModeExchangeAdapter.class, MOCKED_CREATE_DELEGATE_EXCHANGE_ADAPTER);
@@ -931,7 +907,7 @@ public void testGettingExchangeBuyingFeeSuccessfully() throws Exception {
931907
tryModeExchangeAdapter.init(exchangeConfig);
932908

933909
final BigDecimal buyPercentageFee =
934-
delegateExchangeAdapter.getPercentageOfBuyOrderTakenForExchangeFee(MARKET_ID);
910+
tryModeExchangeAdapter.getPercentageOfBuyOrderTakenForExchangeFee(MARKET_ID);
935911
assertEquals(0, buyPercentageFee.compareTo(PERCENTAGE_OF_BUY_ORDER_TAKEN_FOR_EXCHANGE_FEE));
936912

937913
PowerMock.verifyAll();
@@ -943,17 +919,12 @@ public void testGettingExchangeBuyingFeeSuccessfully() throws Exception {
943919

944920
@Test
945921
public void testGettingExchangeSellingFeeSuccessfully() throws Exception {
922+
946923
final BitstampExchangeAdapter delegateExchangeAdapter =
947924
PowerMock.createPartialMockAndInvokeDefaultConstructor(
948925
BitstampExchangeAdapter.class,
949926
MOCKED_GET_PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE);
950927

951-
PowerMock.expectPrivate(
952-
delegateExchangeAdapter,
953-
MOCKED_GET_PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE,
954-
eq(MARKET_ID))
955-
.andReturn(PERCENTAGE_OF_SELL_ORDER_TAKEN_FOR_EXCHANGE_FEE);
956-
957928
final TryModeExchangeAdapter tryModeExchangeAdapter =
958929
PowerMock.createPartialMockAndInvokeDefaultConstructor(
959930
TryModeExchangeAdapter.class, MOCKED_CREATE_DELEGATE_EXCHANGE_ADAPTER);

config/exchange.yaml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,19 @@ exchange:
5757
simulatedCounterCurrency: USD
5858

5959
# The starting balance for the simulation. The simulation starts with this amount for counter currency.
60-
counterCurrencyStartingBalance: 100
60+
simulatedCounterCurrencyStartingBalance: 100
6161

6262
# The base currency which is simulated. This must match your chosen market base currency.
6363
simulatedBaseCurrency: BTC
6464

6565
# The starting balance for the simulation. The simulation starts with this amount for base currency.
66-
baseCurrencyStartingBalance: 2
66+
simulatedBaseCurrencyStartingBalance: 2
67+
68+
# The exchange sell fee percentage for the simulation.
69+
simulatedSellFee: 0.1
70+
71+
# The exchange buy fee percentage for the simulation.
72+
simulatedBuyFee: 0.1
6773

6874
# The adapter which should be used for public API calls.
6975
# All special config values for this adapter must be given in this otherConfig section as well.

0 commit comments

Comments
 (0)