Carry-Coin 架构设计 SymbolLedger (4)

Carry-Coin 套利币本 SymbolLedger 设计,SymbolLedger负责存放套利过程中交易对信息,其中包括symbol在Cex中的各项配置、套利阈值等,Dex中的各种合约信息、阈值、交易参数等

SymbolLedger

一个交易所对应一个SymbolLedger实例,程序启动后通过json配置文件进行加载;

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
/**
* <pre>
* 此类用来存储市面上所有的交易对信息
* 维护方式: 基础信息手工,其他信息通过程序自动获取;
* 更新周期: 定期更新;
* 作用: 以此账簿上的币作为循化基础,再从各交易所拉取对应信息;
* </pre>
* <p> @Date : 2023/3/21 </p>
* <p> @Project : block-farming</p>
*
* <p> @author konbluesky </p>
*/
@Slf4j
@Data
public class SymbolLedger {

/**
* 如是内存模式则不进行数据库持久化
*/
private boolean memoryMode = false;

/**
* 更新symbolPairConfigs时,一定要重新对symbolPairConfigMap和symbolPairConfigMap_symbolKey进行更新
* 否则getSymbolPairConfig会失效
* @param symbolPairConfigs
*/
public void setSymbolPairConfigs(List<SymbolPairConfig> symbolPairConfigs) {
this.symbolPairConfigs = symbolPairConfigs;
for(SymbolPairConfig symbolPairConfig : symbolPairConfigs){
updateSymbolPairConfig(symbolPairConfig);
}
log.info("SymbolLedger更新symbolPairConfigs");
}

private List<SymbolPairConfig> symbolPairConfigs = Lists.newCopyOnWriteArrayList();

private Map<String, SymbolPairConfig> symbolPairConfigMap = Maps.newConcurrentMap();

private Map<String, SymbolPairConfig> symbolPairConfigMap_symbolKey = Maps.newConcurrentMap();
// private Table<String, String, SymbolPairConfig> symbolPairConfigMap = Tables.synchronizedTable(HashBasedTable.create());

public void put(SymbolPairConfig symbolPairConfig) {
symbolPairConfigs.add(symbolPairConfig);
updateSymbolPairConfig(symbolPairConfig);
}

private void updateSymbolPairConfig(SymbolPairConfig symbolPairConfig){
CenterSymbolInfo centerSymbolInfo = symbolPairConfig.getCenterSymbolInfo();
symbolPairConfigMap.put(centerSymbolInfo.getBaseCurrency()
.toLowerCase(), symbolPairConfig);
symbolPairConfigMap_symbolKey.put(centerSymbolInfo.getBaseCurrency()
.toLowerCase() + "/" + centerSymbolInfo.getQuoteCurrency()
.toLowerCase(), symbolPairConfig);
}

@Deprecated
public SymbolPairConfig getSymbolPairConfig(String baseCurrency) {
return symbolPairConfigMap.get(baseCurrency.toLowerCase());
}

public List<SymbolPairConfig> getSymbolPairConfigsBy(String baseCurrency) {
List<SymbolPairConfig> result = Lists.newArrayList();
symbolPairConfigMap_symbolKey.forEach((k, v) -> {
if(k.toLowerCase().startsWith(baseCurrency.toLowerCase())) {
result.add(v);
}
});
return result;
}

public SymbolPairConfig getSymbolPairConfig(String baseCurrency, String quoteCurrency) {
return symbolPairConfigMap_symbolKey.get(baseCurrency.toLowerCase() + "/" + quoteCurrency.toLowerCase());
}
}


SymbolLedger构建的静态工厂,json配置通过carry-config-generator(python)生成

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
/**
* <pre>
* 1. 交易对配置文件工厂,目前实现从HUOBI.txt文件中读取;
* 2. 可以在jar包所在目录下创建config目录,将配置文件放入config目录下,程序会自动读取;
* </pre>
*/
@Slf4j
public class SymbolLedgerFactory {

public static final String SYMBOL_HUOBI_BOOKS_FILE = "HUOBI.txt";

public static final String SYMBOL_XT_BOOKS_FILE = "XT.txt";

public static final String SYMBOL_BINANCE_BOOKS_FILE = "BIAN.txt";

public static final String SYMBOL_MEXC_BOOKS_FILE = "MXCAll.txt";

public static final String SYMBOL_GATEIO_BOOKS_FILE = "GATEIO.txt";

public static final String SYMBOL_KUCOIN_BOOKS_FILE = "KUCOIN.txt";

/**
* TODO 从文件中读取交易对配置,目前只解析部分字段;有需要实时更新的字段到时候从服务器上实时拉取;
* swapStableCoinContractAdd 字段配置决定了与交易所的对的匹配;
*
* @param symbolBooksFile
* @param swapConfig
* @return
*/
private static SymbolLedger create(String symbolBooksFile, SwapConfig swapConfig) {
Preconditions.checkArgument(swapConfig != null);
JSONArray jsonArray = getJsonArrayByFile(symbolBooksFile);
SymbolLedger symbolLedger = new SymbolLedger();
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject item = jsonArray.getJSONObject(i);

// continue to parse the json object
if (Strings.isNullOrEmpty(item.getString("swapContract")) || item.getString("swapStableCoinContract")
.equalsIgnoreCase("cake")) {
continue;
}

SymbolPairConfig symbolPairConfig = new SymbolPairConfig();
symbolPairConfig.setBaseCurrency(item.getString("swapCoinContract"));

int chainId = item.getInteger("chainId") != null ? item.getInteger("chainId") : NetworkEnum.BSC.getChainId();
String stableName = SymbolTokenHelper.getTokenInfoByAddressReturnSymbol(chainId, item.getString("swapStableCoinContractAdd"));

symbolPairConfig.setQuoteCurrency(Currency.USDT.getCurrencyCode());
// symbolPairConfig.setSymbol(symbolPairConfig.getBaseCurrency() + "/" + symbolPairConfig.getQuoteCurrency());

// 链上交易对和cex交易对 分别存储,交易所对,目前统一使用usdt
CenterSymbolInfo centerSymbolInfo = new CenterSymbolInfo();
centerSymbolInfo.setBaseCurrency(symbolPairConfig.getBaseCurrency());
centerSymbolInfo.setQuoteCurrency(Currency.USDT.getCurrencyCode());
centerSymbolInfo.setCexMaxOrderForUSDT(item.getBigDecimal("maxOrderForUsdt") == null ? swapConfig.getGlobalMaxOrderForUsdt() : item.getBigDecimal("maxOrderForUsdt"));
centerSymbolInfo.setCexMinOrderForUSDT(item.getBigDecimal("minOrderForUsdt") == null ? swapConfig.getGlobalMinOrderForUsdt() : item.getBigDecimal("minOrderForUsdt"));
centerSymbolInfo.getAskPosition()
.set(item.getInteger("askPosition") == null || item.getInteger("askPosition") == 0 ? 3 : item.getInteger("askPosition"));

symbolPairConfig.setCenterSymbolInfo(centerSymbolInfo);

// 设置去中心化交易所配置信息;
DecenterSymbolInfo decenterSymbolInfo = new DecenterSymbolInfo();
decenterSymbolInfo.setBaseCurrency(symbolPairConfig.getBaseCurrency())
.setQuoteCurrency(stableName == null ? CoinEnum.getCoin(item.getString("swapStableCoinContractAdd"))
.name() : stableName)
.setMiddleCurrency(item.getString("swapMiddleCoinContract"))
.setDexName(item.getString("dex_name"))
.setDexProtocolVersion(item.getString("liquidity_type"))
// 链id
.setChainIds(Set.of(chainId))
.setStableCoinContractAddress(item.getString("swapStableCoinContractAdd"))
.setMiddleCoinContractAddress(item.getString("swapMiddleCoinContractAdd"))
.setTradeCoinContractAddress(item.getString("swapCoinContractAdd"))
.setSwapContractAddress(item.getString("swapContract"))
.setBurnFee(item.getBigDecimal("burn"))
.setBuyTax(item.getBigDecimal("buyTax"))// 买入税率
.setSellTax(item.getBigDecimal("sellTax"))//卖出税率
// .setSellTax(item.getBigDecimal("burn"))// 卖出税率, 老的配置文件中就是sellTax
.setDexBuySlipPoint(item.getBigDecimal("dexSlipPoint")
.add(swapConfig.getGlobalDexSlipPoint()))
.setStableCoinDecimals(item.getIntValue("stableCoinDecimals"))
.setTradeCoinDecimals(item.getIntValue("coinDecimals"))
.setMiddleCoinDecimals(item.getIntValue("middleCoinDecimals"))
.setMethod(item.getString("method"))
.setBuyPosition(item.getIntValue("currentDepthPosition"))
.setReplyPro(item.getBigDecimal("replyPro")) // 反向利润放大的比例
.setDepthPro(item.getBigDecimal("depthPro")) // 反向深度缩小的比例
.setV3LoopContractAddress(item.getString("lpAdd")) // v3 的loopAddress
.setV3Fee(item.getString("feev3")) // v3 的fee
.setDexMaxOrderForUSDT(item.getBigDecimal("dexMax") == null ? swapConfig.getGlobalMaxOrderForUsdt() : item.getBigDecimal("dexMax")) // 反向最大下单量
.setDexMinOrderForUSDT(item.getBigDecimal("dexMin") == null ? swapConfig.getGlobalMinOrderForUsdt() : item.getBigDecimal("dexMin"));// 反向最小下单量

decenterSymbolInfo.setExtensionDexHandlerConfig(new ExtensionDexHandlerConfig(item));

decenterSymbolInfo.init();
symbolPairConfig.setDecenterSymbolInfo(decenterSymbolInfo);
symbolPairConfig.set_rawJson(item);

symbolLedger.put(symbolPairConfig);
}
return symbolLedger;
}


private static JSONArray getJsonArrayByFile(String symbolHuobiBooksFile) {
File configFile = PathUtil.stairsLoad(symbolHuobiBooksFile, "config");

try {

if (configFile == null) {
log.warn("Not fount SymbolBook config file.{}", symbolHuobiBooksFile);
String tempPath = System.getProperty("java.io.tmpdir") + System.currentTimeMillis();
String tempFile = Paths.get(tempPath, File.separator, symbolHuobiBooksFile)
.toString();
Resource resource = new ClassPathResource(symbolHuobiBooksFile);
InputStream initialStream = resource.getInputStream();
byte[] buffer = new byte[initialStream.available()];
initialStream.read(buffer);
configFile = new File(tempFile);
configFile.getParentFile()
.mkdirs();
Files.write(buffer, configFile);
log.info("Loading default SymbolBook config file from classpath: {} ", resource.getURL());
}


String jsonContext = Joiner.on("")
.join(Files.readLines(configFile, Charsets.UTF_8));

if (JSON.isValidArray(jsonContext)) {
return JSON.parseObject(jsonContext, JSONArray.class);
}

} catch (Exception e) {
log.error(e.getMessage(), e);
throw new SwapException("SymbolBook config loading failed.");

}
return new JSONArray();
}

public static SymbolLedger createXT(SwapConfig swapConfig) {
return create(SYMBOL_XT_BOOKS_FILE, swapConfig);
}

public static SymbolLedger createHuoBi() {
return create(SYMBOL_HUOBI_BOOKS_FILE, new SwapConfig());
}

public static SymbolLedger createGateio() {
return create(SYMBOL_GATEIO_BOOKS_FILE, new SwapConfig());
}

public static SymbolLedger createGateio(SwapConfig swapConfig) {
return create(SYMBOL_GATEIO_BOOKS_FILE, swapConfig);
}

public static SymbolLedger createKucoin(SwapConfig swapConfig) {
return create(SYMBOL_KUCOIN_BOOKS_FILE, swapConfig);
}

public static SymbolLedger createOKex() {
return create(SYMBOL_HUOBI_BOOKS_FILE, new SwapConfig());
}

public static SymbolLedger createOKex(SwapConfig swapConfig) {
return create(SYMBOL_HUOBI_BOOKS_FILE, swapConfig);
}

public static SymbolLedger createHuoBi(SwapConfig swapConfig) {
return create(SYMBOL_HUOBI_BOOKS_FILE, swapConfig);
}

public static SymbolLedger createBinance(SwapConfig swapConfig) {
return create(SYMBOL_BINANCE_BOOKS_FILE, swapConfig);
}

public static SymbolLedger createMEXC(SwapConfig swapConfig) {
return create(SYMBOL_MEXC_BOOKS_FILE, swapConfig);
}

/**
* 将配置文件内容刷入db
* 外部通过SwapControlHolder 来对状态进行控制和判断
*
* @param symbolLedger
* @param exchangeType
* @param isMonitor 开关用来控制swapSymbolPairConfigRecord 记录默认是监听状态还是, 非监听状态,
* 首次程序启动的时候是非监听的,需要web端显式的开启,后续job中动态调整默认都是监听状态的
*/
public static void flushToDb(SymbolLedger symbolLedger, ExchangeType exchangeType, boolean isMonitor) {
if (symbolLedger.isMemoryMode()) {
return;
}
SwapSymbolPairConfigRecordRepository res = SpringUtil.getBean(SwapSymbolPairConfigRecordRepository.class);
res.deleteAllByExchangeType(exchangeType);
res.flush();
List<SwapSymbolPairConfigRecord> all = Lists.newArrayList();
symbolLedger.getSymbolPairConfigs().forEach(symbolPairConfig -> {
SwapSymbolPairConfigRecord swapSymbolPairConfigRecord = null;
if (isMonitor) {
swapSymbolPairConfigRecord = SwapSymbolPairConfigRecord.createLoadMonitor(symbolPairConfig, exchangeType);
SwapControlManager.putItem(exchangeType, symbolPairConfig.getUniDexIdentify(), swapSymbolPairConfigRecord);
} else {
swapSymbolPairConfigRecord = SwapSymbolPairConfigRecord.createUnMonitor(symbolPairConfig, exchangeType);
SwapControlManager.putItem(exchangeType, symbolPairConfig.getUniDexIdentify(), swapSymbolPairConfigRecord);
}
all.add(swapSymbolPairConfigRecord);
});
res.saveAllAndFlush(all);
log.info("Load config from config file, Flush to db is ok ! Record size is :{} ", all.size());
}


/**
* 增量更新配置文件
* 1. 数据库中载入当前交易所币配置
* 2. 从内存中获取当前交易所币配置
* 3. 比较数据库中不在内存的配置,更新状态
* 4. 将关闭的币种组装日志打印出来
*/
public static void incrementalUpdate(SymbolLedger symbolLedger, ExchangeType exchangeType) {
if (symbolLedger.isMemoryMode()) {
return;
}
SwapSymbolPairConfigRecordRepository res = SpringUtil.getBean(SwapSymbolPairConfigRecordRepository.class);
List<SwapSymbolPairConfigRecord> all = res.findAllByExchangeType(exchangeType);

String changeCurrency = "";
int closeCount = 0;
for (SwapSymbolPairConfigRecord swapSymbolPairConfigRecord : all) {
String symbol = swapSymbolPairConfigRecord.getSymbol();
if (symbolLedger.getSymbolPairConfig(symbol.split("/")[0], symbol.split("/")[1]) == null) {
swapSymbolPairConfigRecord.setMonitorStatus(SwapSymbolPairConfigRecord.MonitorStatus.UNMONITOR);

changeCurrency += symbol + ",";
closeCount++;
}
}
res.saveAllAndFlush(all);

String msg = Utils.format("{} 交易所内存币本配置已过滤,总数:{} 关闭数量:{} 关闭币种:{}", exchangeType, all.size(), closeCount, changeCurrency);
log.info(msg);
SwapMessageSender.sendMessage(exchangeType, msg);
log.info("Incremental update config from config file, Flush to db is ok ! Record size is :{} ", all.size());

}

SymbolPairConfig 类, 用来存储某个交易对(ABC/USDT)的配置信息

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
/**
* <p> @Date : 2023/3/21 </p>
* <p> @Project : block-farming</p>
*
* <p> @author konbluesky </p>
*/
@Data
public class SymbolPairConfig {

//TODO 现阶段 symbolInfo 是1-1关系

/**
* 交易对在中心化交易所的信息
*/
private CenterSymbolInfo centerSymbolInfo;

/**
* 交易对在去中心化交易所的信息
*/
private DecenterSymbolInfo decenterSymbolInfo;

/**
* 交易对显示名称 BTC/USDT
*/

public String getSymbol() {
return baseCurrency+"/"+quoteCurrency;
}

/**
* RITE/USDT-RITE/USDT-PancakeV2-UniV2
* @return
*/
public String getUniDexIdentify(){
return centerSymbolInfo.getSymbol() + "-" + decenterSymbolInfo.getSymbol() + "-" + decenterSymbolInfo.getDexName() + "-" + decenterSymbolInfo.getDexProtocolVersion();
}

/**
* 交易对基础货币 BTC
*/
private String baseCurrency;

/**
* 交易对报价货币 USDT
*/
private String quoteCurrency;

/**
* 允许开启的实例数
*/
private AtomicInteger reverseAllowInstanceNum = new AtomicInteger(1);
private AtomicInteger forwardAllowInstanceNum = new AtomicInteger(1);
/**
* 开启翻倍后,设置x分钟激情时间,进行翻倍
*/
private AtomicLong reversePassionTime = new AtomicLong(0);
private AtomicLong forwardPassionTime = new AtomicLong(0);


/**
* 交易对的利润阈值,一般在交易所holder初始化的时候从SwapConfig获取;
* TODO 这里的阈值可基于全局的做覆盖;
*/
private BigDecimal profitThreshold;
private BigDecimal profitThresholdRate;
private BigDecimal _origin_profitThresholdRate;
private JSONObject _rawJson;


public void setProfitThresholdRate(BigDecimal profitThresholdRate) {
this.profitThresholdRate = profitThresholdRate;
this._origin_profitThresholdRate = profitThresholdRate;
}

public void setProfitThresholdRateDouble() {
profitThresholdRate = profitThresholdRate.multiply(new BigDecimal("1.3"));
}

public void resetProfitThresholdRate() {
profitThresholdRate = _origin_profitThresholdRate;
}

/**
* 提现次数自增一次
* Increase WithdrawTime
*/
public void increaseWithdrawTimes() {
centerSymbolInfo.getLastWithdrawTime().set(System.currentTimeMillis());
centerSymbolInfo.increaseWithdrawTimes();
}

public boolean isMiddleCoin() {
return !Strings.isNullOrEmpty(decenterSymbolInfo.getMiddleCoinContractAddress());
}

public String getSymbolNoOblique() {
return getSymbol().replace("/", "");
}

}

CenterSymbolInfo 类, 用来存储某个交易对(ABC/USDT)的Cex配置

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
/**
*
* <p> @Date : 2023/3/20 </p>
* <p> @Project : block-farming</p>
*
* <p> @author konbluesky </p>
*/
@Data
@Slf4j
public class CenterSymbolInfo {

public String getSymbol() {
return baseCurrency + "/" + quoteCurrency;
}

/**
* 注意这里是[交易所]对应的基础货币 BTC
*/
private String baseCurrency;

/**
* 注意这里是[交易所]对应的交易对报价货币 USDT
*/
private String quoteCurrency;

private Double price;

/**
* 交易所体现时一般需要指定链名或者网络名
*/
private String chainName;

/**
* 提现次数
*/
private AtomicInteger withdrawTimes = new AtomicInteger(0);

/**
* 最小提现手续费
*/
private BigDecimal withdrawMinFee = BigDecimal.ZERO;

/**
* 手续费换算成美元
*/
private BigDecimal withdrawMinFee$ = BigDecimal.ZERO;

/**
* 传入实时的单价,计算出手续费绝对刀
* @param unitPrice
*/
public void setWithdrawMinFee$(BigDecimal unitPrice) {
this.withdrawMinFee$ = Utils.getScale(unitPrice.multiply(withdrawMinFee));
}

/**
* 最后一次的提现时间
*/
private AtomicLong lastWithdrawTime = new AtomicLong(System.currentTimeMillis());

/**
* 最后一次下卖单时间
*/
private AtomicLong lastPlaceSellOrderTime = new AtomicLong(System.currentTimeMillis());

private AtomicLong lastPlaceBuyOrderTime = new AtomicLong(System.currentTimeMillis());

/**
* 卖单默认位置,此位置是口语中的卖一卖二,不是数组下标
*/
private AtomicInteger askPosition = new AtomicInteger(0);

/**
* 挂单检查时间(卖单)
*/
private int askOrderWaitSeconds = 5;


/**
* 交易所固定最大下单金额(usdt为单位)
*/
private BigDecimal CexMaxOrderForUSDT;

/**
* 交易所固定最小下单金额(usdt为单位)
*/
private BigDecimal CexMinOrderForUSDT;


/**
* 留币时保存的卖出下单ID;
* remain 留存 sell 出售 orderId 订单ID
*/
private String remainSellOrderId;


/**
* 提现次数自增一次
*/
public void increaseWithdrawTimes() {
withdrawTimes.set(withdrawTimes.get() + 1);
}

}

DecenterSymbolInfo 类, 用来存储某个交易对(ABC/USDT)的Dex配置

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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
/**
* <p> @Date : 2023/3/20 </p>
* <p> @Project : block-farming</p>
*
* <p> @author konbluesky </p>
*/
@Accessors(chain = true)
@Data
@Slf4j
public class DecenterSymbolInfo {

private String dexName;

private String dexProtocolVersion;

public String getSymbol() {
if(Strings.isNullOrEmpty(middleCoinContractAddress)) {
return baseCurrency + "/" + quoteCurrency;
}else{
return baseCurrency + "/" + middleCurrency + "/" + quoteCurrency;
}
}

/**
* 注意这里是[链上]对应的基础货币 BTC
*/
private String baseCurrency;

/**
* 注意这里是[链上]对应的交易对报价货币 USDT
*/
private String quoteCurrency;

/**
* 注意这里是[链上]对应的交易对中间报价货币
*/
private String middleCurrency;

private Set<Integer> chainIds;

/**
* TODO 未来需要支持一个币种在多个链上的支持, 目前只考虑币种只在一个链上活跃
* @return
*/
public Integer getFirstChainId(){
return chainIds.iterator().next();
}

/**************************************************************************************************/
/************************************** 链上的地址配置信息 *******************************************/
/**************************************************************************************************/

/**
* 为了方便存取,围绕token,将常用的信息包装起来
*/
private CurrencyBag stable;

private CurrencyBag trade;

private CurrencyBag middle;

public void init() {
if (stable == null) {
stable = CurrencyBag.of(stableCoinContractAddress, quoteCurrency, getStableCoinDecimals());
}
if (trade == null) {
trade = CurrencyBag.of(tradeCoinContractAddress, baseCurrency, getTradeCoinDecimals());
}
if (middle == null) {
middle = CurrencyBag.of(middleCoinContractAddress, middleCurrency, getMiddleCoinDecimals());
}
}

/**
* 稳定币的合约地址
* USTD,ETH,BTC 等
*/
private String stableCoinContractAddress;

/**
* 中间币的合约地址
* BNB 有自己独立的链的等
*/
private String middleCoinContractAddress;

/**
* 交易币的合约地址
* 即最终操作的目标币地址
* 原代码中的:swapCoinContractAdd
*/
private String tradeCoinContractAddress;

/**
* v3流通性的合约地址
*/
private String v3LoopContractAddress;

/**
* v3手续费的
*/
private String v3Fee;

/**
* 去中心化交易所的合约地址
*/
private String swapContractAddress;

/**
* 反向套利,从蛋糕到交易所,稳定币额度
* bnb对就进行交易所价格查询,转换成对应的bnb
* <pre>
* 声明 BidDepths,bidOnePrice,BidDepthsAllAmount;
* swapStableCoinNumOut = bidOnePrice * BidDepthsAllAmount;
* </pre>
*/
private AtomicDouble swapStableCoinNumOut;

/**
* 非稳定币的兑换数量,个数
*/
private AtomicDouble swapFStableCoinNum;

/**
* 稳定币的兑换数量,个数
*/
private AtomicDouble swapStableCoinNum;

/**
* 价格精度 最小就是0.0001
*/
private String priceDecimals;

/**
* 数量精度 最小就是0.01
*/
private String numDecimals;

/**
* 卖时燃烧的手续费
*/
private BigDecimal burnFee;

/**
* 其他费用,初步用来保存转移费用,有些币种的转移费用
*/
private BigDecimal otherFee = BigDecimal.ZERO;

/**
* 链上买币滑点
*/
private BigDecimal dexBuySlipPoint;

private BigDecimal _origin_dexBuySlipPoint;

/**
* 买时 税率
*/
private BigDecimal buyTax = BigDecimal.ZERO;

/**
* 卖时 税率
*/
private BigDecimal sellTax = BigDecimal.ZERO;

/**
* 交易币的精度,在程序启动时通过TokenClient从链上合约中查询
*/
private int tradeCoinDecimals = 0;

/**
* 稳定币的精度,在程序启动时通过TokenClient从链上合约中查询
*/
private int stableCoinDecimals = 0;

/**
* 中间币的精度,在程序启动时通过TokenClient从链上合约中查询
*/
private int middleCoinDecimals = 0;

/**
* 链上固定最大下单金额(usdt为单位)
*/
private BigDecimal dexMaxOrderForUSDT;

/**
* 链上固定最小下单金额(usdt为单位)
*/
private BigDecimal dexMinOrderForUSDT;

/**
* dex购买状态,用于判断是否蛋糕买了,然后回交易所
* 对于这种情况,要进行停止蛋糕购买及交易所到蛋糕的业务
* 根据交易所充值到账时间 一般需要2分钟,即120秒,那130秒内都必须把价格降
*/
private AtomicLong dexBuyTimestamp = new AtomicLong(0);

private String method;

/**
* 反向套利时,需要监控买盘的数据, 这是买盘的位置
* 对应的是centerSymbolInfo的askPosition
*/
private int buyPosition;
/**
* 反向套利时,深度需要*depthPro ,进行打折
*/
private BigDecimal depthPro;

public DecenterSymbolInfo setDexBuySlipPoint(BigDecimal dexBuySlipPoint) {
this.dexBuySlipPoint = dexBuySlipPoint;
// 保留原始值用于恢复
this._origin_dexBuySlipPoint = dexBuySlipPoint;
return this;
}

private ExtensionDexHandlerConfig extensionDexHandlerConfig;


/**
* 反向套利时的放大比例
*/
private BigDecimal replyPro;


public boolean isV3(){
return getDexProtocolVersion().toLowerCase().contains("v3".toLowerCase());
}
public boolean isV2(){
return getDexProtocolVersion().toLowerCase().contains("v2".toLowerCase());
}



/**
* 对dexBuySlipPoint滑点1.5倍;
*/
public void setDexBuySlipPointDouble() {
dexBuySlipPoint = dexBuySlipPoint.multiply(replyPro);
}

public void resetDexBuySlipPoint() {
dexBuySlipPoint = _origin_dexBuySlipPoint;
}

}
Author

Gavin

Posted on

2024-11-05

Updated on

2024-11-05

Licensed under

Comments