-
Notifications
You must be signed in to change notification settings - Fork 0
/
TradingSimulator.java
137 lines (121 loc) · 5.65 KB
/
TradingSimulator.java
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
package service.trades._tools.simulator;
import service.model.trades.Trade;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
/**
* Uses contrarian trading algorithm to add simulated trades to the current trades got from Bitso.
* This strategy will work by counting the M consecutive upticks and N consecutive downticks.
* A trade that executes at a price that is the same as the price of the trade that executed immediately
* preceding it is known as a “zero tick”.
* An uptick is when a trade executes at a higher price than the most recent non-zero-tick trade before it.
* A downtick is when a trade executes at a lower price than the most recent non-zero-tick trade before it.
* After M consecutive upticks, the algorithm should sell 1 BTC at the price of the most recent uptick.
* After N consecutive downticks, it should buy 1 BTC at the price of the most recent downtick.
*/
public class TradingSimulator {
private int downticksToBuy;
private int upticksToSell;
private TickCounter tickCounter;
/**
* Creates a Trading Simulator specifying the upticks necessary to create a sell trade as well as
* the downticks necessary to add a buy trade.
* @param upticksToSell number of upticks that will make a sell trade be inserted in the current trades
* @param downticksToBuy number of downticks that will make a buy trade be inserted in the current trades
*/
public TradingSimulator(int upticksToSell, int downticksToBuy) {
tickCounter = TickCounter.getInstance();
this.upticksToSell = upticksToSell;
this.downticksToBuy = downticksToBuy;
}
/**
* Tries to add a buy or sell trade evaluating the current upticks and downticks and comparing them
* against upticksToSell and downticksToBuy respectively.
* If an uptick is detected , then the previous downticks are restarted and vice versa.
* @param lastTrade is the last trade added to the current trades used to compare against the first new trade.
* @param newTrades are the new trades retrieved from Bitso Rest Api service.
* @return the list of trades containing all new trades and any new simulated trade that may be added
* using the contrarian algorithm regarding upticks and downticks.
*/
public List<Trade> addSimulatedTrades(Trade lastTrade, List<Trade> newTrades) {
if (newTrades != null && !newTrades.isEmpty()) {
List<Trade> tradesWithSimulated = new ArrayList<>(singletonList(newTrades.get(0)));
addSimulatedTradeFromLastTrade(tradesWithSimulated, lastTrade, newTrades);
addSimulatedTradesFromNewTrades(tradesWithSimulated, newTrades);
return tradesWithSimulated;
}
return emptyList();
}
/**
* Updates the number of upticks necessary to add a sell trade to the current trades.
* @param upticksToSell is the number of upticks necessary to add a sell trade to the current trades
*/
public void setUpticksToSell(int upticksToSell) {
this.upticksToSell = upticksToSell;
}
/**
* Updates the number of downticks necessary to add a buy trade to the current trades.
* @param downticksToBuy is the number of downticks necessary to add a buy trade to the current trades
*/
public void setDownticksToBuy(int downticksToBuy) {
this.downticksToBuy = downticksToBuy;
}
private void addSimulatedTradeFromLastTrade(List<Trade> tradesWithSimulated, Trade lastTrade, List<Trade> tradeList) {
if (lastTrade != null) {
compareAndAddSimulatedTrade(tradeList.get(0), lastTrade, tradesWithSimulated);
}
}
private void compareAndAddSimulatedTrade(Trade trade, Trade lastTrade, List<Trade> tradesWithSimulated) {
int compare = getPrice(trade).compareTo(getPrice(lastTrade));
Lock lock = new ReentrantLock();
if (compare > 0) {
addSellTrade(trade, tradesWithSimulated, lock);
} else if (compare < 0) {
addBuyTrade(trade, tradesWithSimulated, lock);
}
}
private void addSellTrade(Trade trade, List<Trade> tradesWithSimulated, Lock lock) {
try {
lock.lock();
if (upticksToSell > 0 && tickCounter.uptick() == upticksToSell) {
tradesWithSimulated.add(trade.withSimulatedAndMarkerSide(true, "sell"));
tickCounter.reset();
}
} finally {
lock.unlock();
}
}
private void addBuyTrade(Trade trade, List<Trade> tradesWithSimulated, Lock lock) {
try {
lock.lock();
if (downticksToBuy > 0 && tickCounter.downtick() == downticksToBuy) {
tradesWithSimulated.add(trade.withSimulatedAndMarkerSide(true, "buy"));
tickCounter.reset();
}
} finally {
lock.unlock();
}
}
private BigDecimal getPrice(Trade trade) {
return new BigDecimal(trade.getPrice());
}
private void addSimulatedTradesFromNewTrades(List<Trade> tradesWithSimulated, List<Trade> newTrades) {
for (int i = 1; i < newTrades.size(); i++) {
tradesWithSimulated.add(newTrades.get(i));
compareAndAddSimulatedTrade(newTrades.get(i), newTrades.get(i - 1), tradesWithSimulated);
}
}
void setTickCounter(TickCounter tickCounter) {
this.tickCounter = tickCounter;
}
/**
* To be used only for tests.
*/
public void resetCounter() {
tickCounter.reset();
}
}