Skip to content

Commit 7815aea

Browse files
committed
Merging final changes to main
2 parents b9142dd + 65c539d commit 7815aea

7 files changed

Lines changed: 425 additions & 28 deletions

File tree

algo-exercise/algo/src/main/java/codingblackfemales/sotw/SimpleAlgoState.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,6 @@ public interface SimpleAlgoState {
2020
public List<ChildOrder> getActiveChildOrders();
2121

2222
public long getInstrumentId();
23+
24+
2325
}

algo-exercise/getting-started/README.md

Whitespace-only changes.
Lines changed: 150 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,175 @@
11
package codingblackfemales.gettingstarted;
22

33
import codingblackfemales.action.Action;
4+
import codingblackfemales.action.CancelChildOrder;
5+
import codingblackfemales.action.CreateChildOrder;
46
import codingblackfemales.action.NoAction;
57
import codingblackfemales.algo.AlgoLogic;
8+
import codingblackfemales.sotw.ChildOrder;
69
import codingblackfemales.sotw.SimpleAlgoState;
10+
import codingblackfemales.sotw.marketdata.AskLevel;
11+
import codingblackfemales.sotw.marketdata.BidLevel;
712
import codingblackfemales.util.Util;
13+
import messages.order.Side;
814
import org.slf4j.Logger;
915
import org.slf4j.LoggerFactory;
1016

17+
import java.util.Iterator;
18+
1119
public class MyAlgoLogic implements AlgoLogic {
1220

1321
private static final Logger logger = LoggerFactory.getLogger(MyAlgoLogic.class);
22+
// Thresholds for buy and sell actions
23+
private static long buyThreshold = 95; // Minimum acceptable bid price for a buy order
24+
private static long sellThreshold = 115; // Minimum acceptable ask price for a sell order
25+
private static long spreadThreshold = -3; // Minimum spread threshold for action
26+
27+
1428

1529
@Override
1630
public Action evaluate(SimpleAlgoState state) {
1731

1832
var orderBookAsString = Util.orderBookToString(state);
1933

34+
//shows current state of order book and current state of active orders
2035
logger.info("[MYALGO] The state of the order book is:\n" + orderBookAsString);
36+
logger.info("Active Orders:" + state.getActiveChildOrders().toString());
37+
38+
var totalOrderCount = state.getChildOrders().size();
39+
40+
//make sure we have an exit condition...
41+
if (totalOrderCount > 10) {
42+
return NoAction.NoAction;
43+
}
44+
45+
46+
47+
final BidLevel bidlevel = state.getBidAt(0);
48+
final long bestBidPrice = bidlevel.price;
49+
final long bidQuantity = bidlevel.quantity;
50+
51+
52+
53+
final AskLevel asklevel = state.getAskAt(0);
54+
final long bestAskPrice = asklevel.price;
55+
final long askQuantity = asklevel.quantity;
56+
57+
58+
//calculate the spread
59+
final long spread = bestAskPrice - bestBidPrice;
60+
logger.info("[MYALGO] Spread between ask and bid " + spread);
61+
62+
//calculate the midPrice
63+
final long midPrice =(bestBidPrice + bestBidPrice)/2;
64+
logger.info("[MYALGO] Mid-price calculated:" + midPrice);
65+
66+
//Calculate the spread as a percentage of the mid-price
67+
final double spreadPercentage = (double) spread / midPrice * 100;
68+
logger.info("[MYALGO] Spread percentage: " + spreadPercentage + "%");
69+
70+
// Check for matching orders
71+
matchOrders(state, bestBidPrice, bestAskPrice);
72+
73+
74+
75+
// not to create or cancel orders because spread is small
76+
if (spread < spreadThreshold) {
77+
logger.info("[MYALGO] The spread is small " + spread + " No Action");
78+
return NoAction.NoAction;
79+
80+
}
81+
82+
83+
//count the buy and sell orders
84+
long buyOrdersCount = state.getChildOrders().stream().filter(ChildOrder -> ChildOrder.getSide() == Side.BUY).count();
85+
long sellOrdersCount = state.getChildOrders().stream().filter(ChildOrder -> ChildOrder.getSide() == Side.SELL).count();
86+
87+
88+
89+
//create new buy orders if there are less than 5 orders
90+
if (buyOrdersCount < 5 ) {
91+
return createBuyOrder(state, buyOrdersCount, bidQuantity, bestBidPrice);
92+
}
2193

22-
/********
23-
*
24-
* Add your logic here....
25-
*
26-
*/
94+
//create new sell orders if there are less than 5 order
95+
if (sellOrdersCount < 5 ) {
96+
return createSellOrder(state, sellOrdersCount, askQuantity, bestAskPrice);
97+
}
98+
99+
//cancel orders that don't match the best price
100+
101+
return cancelOrders(state, bestBidPrice, bestAskPrice);
102+
}
103+
104+
private void matchOrders(SimpleAlgoState state, long bestBidPrice, long bestAskPrice) {
105+
Iterator<ChildOrder> iterator = state.getActiveChildOrders().iterator();
106+
107+
while (iterator.hasNext()) {
108+
ChildOrder order = iterator.next();
109+
if (order.getSide() == Side.BUY && order.getPrice() >= bestAskPrice) {
110+
logger.info("[MYALGO] Matched BUY order: Order ID: #" + order.getOrderId() +", Side: " + order.getSide() + ", Price: " + order.getPrice() + ", Quantity: " + order.getQuantity());
111+
iterator.remove(); // Remove matched order
112+
} else if (order.getSide() == Side.SELL && order.getPrice() <= bestBidPrice) {
113+
logger.info("[MYALGO] Matched SELL order: Order ID: #" + order.getOrderId() +", Side: " + order.getSide() + ", Price: " + order.getPrice() + ", Quantity: " + order.getQuantity());
114+
iterator.remove(); // Remove matched order
115+
}
116+
}
117+
}
118+
119+
120+
121+
122+
123+
//create method to create new buy order
124+
public Action createBuyOrder(SimpleAlgoState state, long buyOrdersCount, long bidQuantity, long bestBidPrice) {
125+
logger.info("[MYALGO] Creating new buy order: " + state.getChildOrders().size() + " orders and add to new buy order " + bidQuantity + " @ " + bestBidPrice);
126+
logger.info(state.getActiveChildOrders().toString());
127+
logger.info("Buy order count is:" + buyOrdersCount);
128+
//creates a new child order
129+
return new CreateChildOrder(Side.BUY, bidQuantity, bestBidPrice);
130+
131+
}
132+
133+
// create a method to create a new sell order
134+
public Action createSellOrder(SimpleAlgoState state, long sellOrdersCount, long askQuantity, long bestAskPrice) {
135+
logger.info("[MYALGO] Creating new sell order:" + state.getChildOrders().size() + " orders and add to new sell order " + askQuantity + " @ " + bestAskPrice);
136+
logger.info("Sell order count is:" + sellOrdersCount);
137+
//creates a new child order
138+
return new CreateChildOrder(Side.SELL, askQuantity, bestAskPrice);
139+
}
140+
141+
//create a method to cancel orders that don't match best price or fall below thresholds
142+
public Action cancelOrders(SimpleAlgoState state, long bestBidPrice, long bestAskPrice){
143+
for (ChildOrder order : state.getActiveChildOrders()){
144+
logger.info("Order ID: #" + order.getOrderId() + ", Side: " + order.getSide() + ", Price: " + order.getPrice() + ", Quantity: " + order.getQuantity());
145+
146+
boolean buyOrder = order.getSide() ==Side.BUY;
147+
boolean notBuyThreshold = order.getPrice() != buyThreshold;
148+
boolean lessThanBuyThreshold = order.getPrice() < buyThreshold;
149+
150+
// Cancel buy orders not matching the best price or below the buy threshold
151+
if (buyOrder && (notBuyThreshold || lessThanBuyThreshold)) {
152+
logger.info(String.format("The buy Threshold is %d", + buyThreshold ));
153+
logger.info(String.format("[MYALGO] Cancel BUY order %d with price %d and quantity %d. The current best bid price is %d." ,+ order.getOrderId(), + order.getPrice(), + order.getQuantity(), + bestBidPrice));
154+
return new CancelChildOrder(order);
155+
}
156+
boolean sellTheOrder = order.getSide() ==Side.SELL;
157+
boolean notSellThreshold = order.getPrice() != sellThreshold;
158+
boolean lessThanSellThreshold = order.getPrice() < sellThreshold;
159+
160+
161+
// Cancel sell orders not matching the best price or below the sell threshold
162+
if (sellTheOrder && (notSellThreshold || lessThanSellThreshold)) {
163+
logger.info(String.format("The sell Threshold is %d", + sellThreshold ));
164+
logger.info(String.format("[MYALGO] Cancel SELL order %d with price %d and quantity %d. The current best ask price is %d." ,+ order.getOrderId(), + order.getPrice(), + order.getQuantity(), + bestAskPrice));
165+
return new CancelChildOrder(order);
166+
}
167+
}
27168

28169
return NoAction.NoAction;
29170
}
30171
}
172+
173+
174+
175+

algo-exercise/getting-started/src/test/java/codingblackfemales/gettingstarted/AbstractAlgoBackTest.java

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public Sequencer getSequencer() {
5555

5656
public abstract AlgoLogic createAlgoLogic();
5757

58-
protected UnsafeBuffer createTick(){
58+
protected UnsafeBuffer createTick() {
5959
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
6060
final BookUpdateEncoder encoder = new BookUpdateEncoder();
6161

@@ -87,7 +87,7 @@ protected UnsafeBuffer createTick(){
8787
return directBuffer;
8888
}
8989

90-
protected UnsafeBuffer createTick2(){
90+
protected UnsafeBuffer createTick2() {
9191

9292
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
9393
final BookUpdateEncoder encoder = new BookUpdateEncoder();
@@ -119,5 +119,52 @@ protected UnsafeBuffer createTick2(){
119119
return directBuffer;
120120
}
121121

122+
protected UnsafeBuffer createTickWithHighThreshold() {
123+
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
124+
final BookUpdateEncoder encoder = new BookUpdateEncoder();
125+
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
126+
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);
127+
128+
// Encode the message
129+
encoder.wrapAndApplyHeader(directBuffer, 0, headerEncoder);
130+
131+
// Set a high spread by configuring ask and bid levels
132+
encoder.venue(Venue.XLON);
133+
encoder.instrumentId(123L);
134+
135+
// Here, we’ll set a very high ask and a much lower bid.
136+
encoder.askBookCount(1).next().price(150L).size(300L); // High ask price
137+
encoder.bidBookCount(1).next().price(100L).size(300L); // Low bid price
138+
139+
encoder.instrumentStatus(InstrumentStatus.CONTINUOUS);
140+
encoder.source(Source.STREAM);
141+
142+
return directBuffer;
143+
}
144+
145+
protected UnsafeBuffer createTickWithIncreasingVolume() {
146+
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
147+
final BookUpdateEncoder encoder = new BookUpdateEncoder();
148+
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
149+
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);
150+
151+
encoder.wrapAndApplyHeader(directBuffer, 0, headerEncoder);
152+
encoder.venue(Venue.XLON);
153+
encoder.instrumentId(123L);
154+
155+
encoder.askBookCount(1)
156+
.next().price(110L).size(1000L); // Higher ask quantity
157+
158+
encoder.bidBookCount(1)
159+
.next().price(108L).size(900L); // Higher bid quantity
160+
161+
encoder.instrumentStatus(InstrumentStatus.CONTINUOUS);
162+
encoder.source(Source.STREAM);
163+
164+
return directBuffer;
165+
}
166+
167+
168+
122169

123170
}

algo-exercise/getting-started/src/test/java/codingblackfemales/gettingstarted/AbstractAlgoTest.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,73 @@ protected UnsafeBuffer createTick(){
7575
return directBuffer;
7676
}
7777

78+
protected UnsafeBuffer createTickWithHighThreshold() {
79+
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
80+
final BookUpdateEncoder encoder = new BookUpdateEncoder();
81+
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
82+
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);
83+
84+
85+
encoder.wrapAndApplyHeader(directBuffer, 0, headerEncoder);
86+
87+
88+
encoder.venue(Venue.XLON);
89+
encoder.instrumentId(123L);
90+
91+
// Here, we’ll set a very high ask and a much lower bid.
92+
encoder.askBookCount(1).next().price(150L).size(300L); // High ask price
93+
encoder.bidBookCount(1).next().price(100L).size(300L); // Low bid price
94+
95+
encoder.instrumentStatus(InstrumentStatus.CONTINUOUS);
96+
encoder.source(Source.STREAM);
97+
98+
return directBuffer;
99+
}
100+
101+
protected UnsafeBuffer createTickWithLowThreshold() {
102+
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
103+
final BookUpdateEncoder encoder = new BookUpdateEncoder();
104+
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
105+
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);
106+
107+
encoder.wrapAndApplyHeader(directBuffer, 0, headerEncoder);
108+
encoder.venue(Venue.XLON);
109+
encoder.instrumentId(123L);
110+
111+
encoder.askBookCount(1)
112+
.next().price(100L).size(200L); // Ask price close to bid
113+
114+
encoder.bidBookCount(1)
115+
.next().price(90L).size(200L); // Bid price close to ask
116+
117+
encoder.instrumentStatus(InstrumentStatus.CONTINUOUS);
118+
encoder.source(Source.STREAM);
119+
120+
return directBuffer;
121+
}
122+
123+
protected UnsafeBuffer createTickWithIncreasingVolume() {
124+
final MessageHeaderEncoder headerEncoder = new MessageHeaderEncoder();
125+
final BookUpdateEncoder encoder = new BookUpdateEncoder();
126+
final ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
127+
final UnsafeBuffer directBuffer = new UnsafeBuffer(byteBuffer);
128+
129+
encoder.wrapAndApplyHeader(directBuffer, 0, headerEncoder);
130+
encoder.venue(Venue.XLON);
131+
encoder.instrumentId(123L);
132+
133+
encoder.askBookCount(1)
134+
.next().price(110L).size(1000L); // Higher ask quantity
135+
136+
encoder.bidBookCount(1)
137+
.next().price(108L).size(900L); // Higher bid quantity
138+
139+
encoder.instrumentStatus(InstrumentStatus.CONTINUOUS);
140+
encoder.source(Source.STREAM);
141+
142+
return directBuffer;
143+
}
144+
78145

79146

80147
}

0 commit comments

Comments
 (0)