Skip to content

Commit 9deb4c9

Browse files
committed
add simple ticker example based on json rpc
1 parent d761137 commit 9deb4c9

3 files changed

Lines changed: 152 additions & 0 deletions

File tree

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ add_executable(example-get-account-info examples/getAccountInfo.cpp)
1414
add_executable(example-account-subscribe examples/accountSubscribe.cpp)
1515
add_executable(example-send-transaction examples/sendTransaction.cpp)
1616
add_executable(example-place-order examples/placeOrder.cpp)
17+
add_executable(example-ticker examples/ticker.cpp)
1718

1819
# link
1920
target_link_libraries(example-get-account-info ${CONAN_LIBS})
2021
target_link_libraries(example-account-subscribe ${CONAN_LIBS})
2122
target_link_libraries(example-send-transaction ${CONAN_LIBS})
2223
target_link_libraries(example-place-order ${CONAN_LIBS})
24+
target_link_libraries(example-ticker ${CONAN_LIBS})
2325

2426
# tests
2527
include(CTest)

examples/ticker.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <chrono>
2+
#include <cpr/cpr.h>
3+
#include <nlohmann/json.hpp>
4+
using json = nlohmann::json;
5+
6+
#include "../mango_v3.hpp"
7+
8+
int main()
9+
{
10+
const auto &config = mango_v3::MAINNET;
11+
const auto group = solana::rpc::getAccountInfo<mango_v3::MangoGroup>(
12+
config.endpoint,
13+
config.group);
14+
15+
const auto symbolIt = std::find(config.symbols.begin(), config.symbols.end(), "BTC");
16+
const auto marketIndex = symbolIt - config.symbols.begin();
17+
assert(config.symbols[marketIndex] == "BTC");
18+
19+
const auto perpMarketPk = group.perpMarkets[marketIndex].perpMarket;
20+
21+
const auto market = solana::rpc::getAccountInfo<mango_v3::PerpMarket>(
22+
config.endpoint,
23+
perpMarketPk.toBase58());
24+
assert(market.mangoGroup.toBase58() == config.group);
25+
26+
while (true)
27+
{
28+
const auto bids = solana::rpc::getAccountInfo<mango_v3::BookSide>(config.endpoint, market.bids.toBase58());
29+
30+
auto bidsIt = mango_v3::BookSide::iterator(mango_v3::Side::Buy, bids);
31+
while (bidsIt.stack.size() > 0)
32+
{
33+
if ((*bidsIt).tag == mango_v3::NodeType::LeafNode)
34+
{
35+
const auto leafNode = reinterpret_cast<const struct mango_v3::LeafNode *>(&(*bidsIt));
36+
const auto now = std::chrono::system_clock::now();
37+
const auto nowUnix = chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count();
38+
const auto isValid = !leafNode->timeInForce || leafNode->timestamp + leafNode->timeInForce < nowUnix;
39+
if (isValid)
40+
{
41+
std::cout << "best bid prz:" << (uint64_t)(leafNode->key >> 64) << " qty:" << leafNode->quantity << std::endl;
42+
break;
43+
}
44+
}
45+
++bidsIt;
46+
}
47+
}
48+
}

mango_v3.hpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ namespace mango_v3
1515
const int QUOTE_INDEX = 15;
1616
const int EVENT_SIZE = 200;
1717
const int EVENT_QUEUE_SIZE = 256;
18+
const int BOOK_NODE_SIZE = 88;
19+
const int BOOK_SIZE = 1024;
1820

1921
struct Config
2022
{
@@ -218,6 +220,106 @@ namespace mango_v3
218220
AnyEvent items[EVENT_QUEUE_SIZE];
219221
};
220222

223+
enum NodeType : uint32_t
224+
{
225+
Uninitialized = 0,
226+
InnerNode,
227+
LeafNode,
228+
FreeNode,
229+
LastFreeNode
230+
};
231+
232+
struct AnyNode
233+
{
234+
NodeType tag;
235+
uint8_t padding[BOOK_NODE_SIZE - 4];
236+
};
237+
238+
struct InnerNode
239+
{
240+
NodeType tag;
241+
uint32_t prefixLen;
242+
__uint128_t key;
243+
uint32_t children[2];
244+
uint8_t padding[BOOK_NODE_SIZE - 32];
245+
};
246+
247+
struct LeafNode
248+
{
249+
NodeType tag;
250+
uint8_t ownerSlot;
251+
uint8_t orderType;
252+
uint8_t version;
253+
uint8_t timeInForce;
254+
__uint128_t key;
255+
solana::PublicKey owner;
256+
uint64_t quantity;
257+
uint64_t clientOrderId;
258+
uint64_t bestInitial;
259+
uint64_t timestamp;
260+
};
261+
262+
struct FreeNode
263+
{
264+
NodeType tag;
265+
uint32_t next;
266+
uint8_t padding[BOOK_NODE_SIZE - 8];
267+
};
268+
269+
struct BookSide
270+
{
271+
MetaData metaData;
272+
uint64_t bumpIndex;
273+
uint64_t freeListLen;
274+
uint32_t freeListHead;
275+
uint32_t rootNode;
276+
uint64_t leafCount;
277+
AnyNode nodes[BOOK_SIZE];
278+
279+
struct iterator
280+
{
281+
Side side;
282+
const BookSide &bookSide;
283+
std::stack<uint32_t> stack;
284+
uint32_t left, right;
285+
286+
iterator(Side side, const BookSide &bookSide)
287+
: side(side), bookSide(bookSide)
288+
{
289+
stack.push(bookSide.rootNode);
290+
left = side == Side::Buy ? 1 : 0;
291+
right = side == Side::Buy ? 0 : 1;
292+
}
293+
294+
bool operator==(const iterator &other) const
295+
{
296+
return &bookSide == &other.bookSide && stack.top() == other.stack.top();
297+
}
298+
299+
iterator &operator++()
300+
{
301+
if (stack.size() > 0)
302+
{
303+
const auto &elem = **this;
304+
stack.pop();
305+
306+
if (elem.tag == NodeType::InnerNode)
307+
{
308+
const auto innerNode = reinterpret_cast<const struct InnerNode *>(&elem);
309+
stack.push(innerNode->children[right]);
310+
stack.push(innerNode->children[left]);
311+
}
312+
}
313+
return *this;
314+
}
315+
316+
const AnyNode &operator*() const
317+
{
318+
return bookSide.nodes[stack.top()];
319+
}
320+
};
321+
};
322+
221323
#pragma pack(pop)
222324

223325
// instructions are even tighter packed, every byte counts

0 commit comments

Comments
 (0)