1
+ #include " auto-trade-processor.hpp"
2
+
3
+ #include < thread>
4
+
5
+ #include " auto-trade-options.hpp"
6
+ #include " cct_exception.hpp"
7
+ #include " coincenterinfo.hpp"
8
+ #include " timestring.hpp"
9
+
10
+ namespace cct {
11
+
12
+ AutoTradeProcessor::AutoTradeProcessor (const AutoTradeOptions &autoTradeOptions)
13
+ : _exchangeStatusVector(autoTradeOptions.publicExchanges().size()) {
14
+ int publicExchangePos = 0 ;
15
+ for (const auto &[exchangeNameEnum, publicExchangeAutoTradeOptions] : autoTradeOptions) {
16
+ ExchangeStatus &selectedExchangesStatus = _exchangeStatusVector[publicExchangePos];
17
+ selectedExchangesStatus.pPublicExchangeAutoTradeOptions = &publicExchangeAutoTradeOptions;
18
+ selectedExchangesStatus.exchangeNameEnum = exchangeNameEnum;
19
+ for (const auto &[market, marketAutoTradeOptions] : publicExchangeAutoTradeOptions) {
20
+ MarketStatus &marketStatus = selectedExchangesStatus.marketStatusVector .emplace_back ();
21
+
22
+ marketStatus.market = market;
23
+ marketStatus.pMarketAutoTradeOptions = &marketAutoTradeOptions;
24
+
25
+ for (std::string_view account : marketAutoTradeOptions.accounts ) {
26
+ marketStatus.privateExchangeNames .emplace_back (exchangeNameEnum, account);
27
+ }
28
+ }
29
+ ++publicExchangePos;
30
+ }
31
+ }
32
+
33
+ namespace {
34
+ const auto &GetAutoTradeMarketConfig (Market market, const auto &publicExchangeAutoTradeOptions) {
35
+ const auto it = publicExchangeAutoTradeOptions.find (market);
36
+ if (it == publicExchangeAutoTradeOptions.end ()) {
37
+ throw exception (" Should not happen - market not found in account auto trade options" );
38
+ }
39
+ return it->second ;
40
+ }
41
+
42
+ bool IsQueryTooEarly (TimePoint nowTs, const auto &marketStatus, const auto &publicExchangeAutoTradeOptions) {
43
+ const auto &marketAutoTradeOptions = GetAutoTradeMarketConfig (marketStatus.market , publicExchangeAutoTradeOptions);
44
+ return marketStatus.lastQueryTime + marketAutoTradeOptions.repeatTime .duration > nowTs;
45
+ }
46
+ } // namespace
47
+
48
+ AutoTradeProcessor::SelectedMarketVector AutoTradeProcessor::computeSelectedMarkets () {
49
+ SelectedMarketVector selectedMarketVector;
50
+
51
+ const auto ts = Clock::now ();
52
+
53
+ TimePoint earliestQueryTime = TimePoint::max ();
54
+
55
+ for (ExchangeStatus &exchangeStatus : _exchangeStatusVector) {
56
+ const auto &publicExchangeAutoTradeOptions = *exchangeStatus.pPublicExchangeAutoTradeOptions ;
57
+
58
+ auto &marketStatusVector = exchangeStatus.marketStatusVector ;
59
+
60
+ if (marketStatusVector.empty ()) {
61
+ continue ;
62
+ }
63
+
64
+ // Sort markets by ascending last query time, discarding those (placed at the end of the vector) which cannot be
65
+ // queried right now
66
+ std::ranges::sort (marketStatusVector,
67
+ [ts, &publicExchangeAutoTradeOptions](const MarketStatus &lhs, const MarketStatus &rhs) {
68
+ const bool lhsIsTooEarly = IsQueryTooEarly (ts, lhs, publicExchangeAutoTradeOptions);
69
+ const bool rhsIsTooEarly = IsQueryTooEarly (ts, rhs, publicExchangeAutoTradeOptions);
70
+
71
+ if (lhsIsTooEarly != rhsIsTooEarly) {
72
+ return !lhsIsTooEarly;
73
+ }
74
+
75
+ return lhs.lastQueryTime < rhs.lastQueryTime ;
76
+ });
77
+
78
+ MarketStatus &selectedMarketStatus = marketStatusVector.front ();
79
+ if (IsQueryTooEarly (ts, selectedMarketStatus, publicExchangeAutoTradeOptions)) {
80
+ const auto repeatTime =
81
+ GetAutoTradeMarketConfig (selectedMarketStatus.market , publicExchangeAutoTradeOptions).repeatTime .duration ;
82
+ earliestQueryTime = std::min (earliestQueryTime, selectedMarketStatus.lastQueryTime + repeatTime);
83
+ continue ;
84
+ }
85
+
86
+ selectedMarketStatus.lastQueryTime = ts;
87
+ selectedMarketVector.emplace_back (selectedMarketStatus.privateExchangeNames , selectedMarketStatus.market );
88
+ }
89
+
90
+ if (selectedMarketVector.empty () && earliestQueryTime != TimePoint::max ()) {
91
+ log ::debug (" Sleeping until {}" , TimeToString (earliestQueryTime));
92
+ std::this_thread::sleep_until (earliestQueryTime + std::chrono::milliseconds (1 ));
93
+ selectedMarketVector = computeSelectedMarkets ();
94
+ if (selectedMarketVector.empty ()) {
95
+ log ::error (" Waiting sufficient time should return at least one market for the next turn" );
96
+ }
97
+ }
98
+
99
+ return selectedMarketVector;
100
+ }
101
+
102
+ AutoTradeProcessor::MarketTraderEngines AutoTradeProcessor::createMarketTraderEngines (
103
+ const CoincenterInfo &coincenterInfo) const {
104
+ MarketTraderEngines marketTraderEngines;
105
+
106
+ for (const ExchangeStatus &exchangeStatus : _exchangeStatusVector) {
107
+ const schema::AutoTradeExchangeConfig &publicExchangeAutoTradeOptions =
108
+ *exchangeStatus.pPublicExchangeAutoTradeOptions ;
109
+
110
+ const ExchangeNameEnum exchangeNameEnum = exchangeStatus.exchangeNameEnum ;
111
+ const schema::ExchangeConfig &exchangeConfig = coincenterInfo.exchangeConfig (exchangeNameEnum);
112
+
113
+ auto &marketTraderEngineMap = marketTraderEngines[static_cast <int >(exchangeNameEnum)];
114
+
115
+ for (const MarketStatus &marketStatus : exchangeStatus.marketStatusVector ) {
116
+ const auto marketIt = publicExchangeAutoTradeOptions.find (marketStatus.market );
117
+ if (marketIt == publicExchangeAutoTradeOptions.end ()) {
118
+ throw exception (" Should not happen - market not found in account auto trade options" );
119
+ }
120
+ const schema::AutoTradeMarketConfig &autoTradeMarketConfig = marketIt->second ;
121
+
122
+ const auto [it, isInserted] = marketTraderEngineMap.emplace (
123
+ marketStatus.market ,
124
+ MarketTraderEngine (exchangeConfig, marketStatus.market , autoTradeMarketConfig.baseStartAmount ,
125
+ autoTradeMarketConfig.quoteStartAmount ));
126
+ if (!isInserted) {
127
+ throw exception (" Should not happen - market already exists in market trader engines" );
128
+ }
129
+ }
130
+ }
131
+
132
+ return marketTraderEngines;
133
+ }
134
+
135
+ } // namespace cct
0 commit comments