More fixes
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
package com.fanitas.evelyn.core;
|
package com.fanitas.evelyn.core;
|
||||||
|
|
||||||
import com.r35157.libs.basic.Pair;
|
import com.r35157.libs.basic.Pair;
|
||||||
|
import com.r35157.libs.valuetypes.basic.AssetPrice;
|
||||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@@ -8,21 +9,21 @@ import java.util.HashMap;
|
|||||||
|
|
||||||
public interface DesiredPositionCalculator {
|
public interface DesiredPositionCalculator {
|
||||||
|
|
||||||
HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, MoneyAmount> calculateRebalancingProposal(
|
HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ>> calculateRebalancingProposal(
|
||||||
ΩPriceΩ currentPrice,
|
ΩSyrupPriceΩ currentPrice,
|
||||||
MoneyAmount totalReadyAmountMintA,
|
ΩSolanaAmountΩ totalReadyAmountMintA,
|
||||||
MoneyAmount totalReadyAmountMintB
|
ΩSyrupAmountΩ totalReadyAmountMintB
|
||||||
);
|
);
|
||||||
|
|
||||||
Pair<MoneyAmount, MoneyAmount> calculateTotalDistributedSums();
|
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> calculateTotalDistributedSums();
|
||||||
|
|
||||||
Pair<MoneyAmount, MoneyAmount> calculateLockedSums(ΩPriceΩ currentPrice);
|
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> calculateLockedSums(ΩSyrupPriceΩ currentPrice);
|
||||||
|
|
||||||
Pair<MoneyAmount, MoneyAmount> calculateRedistributableSums(
|
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> calculateRedistributableSums(
|
||||||
ΩPriceΩ currentPrice,
|
ΩSyrupPriceΩ currentPrice,
|
||||||
MoneyAmount inactiveInAccountMintA,
|
ΩSolanaAmountΩ inactiveInAccountMintA,
|
||||||
MoneyAmount inactiveInAccountMintB,
|
ΩSolanaAmountΩ reservedForBurnMintA,
|
||||||
MoneyAmount reservedForBurnMintA,
|
ΩSyrupAmountΩ inactiveInAccountMintB,
|
||||||
MoneyAmount reservedForBurnMintB
|
ΩSyrupAmountΩ reservedForBurnMintB
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+137
-114
@@ -4,8 +4,10 @@ import com.r35157.libs.basic.Pair;
|
|||||||
import com.fanitas.evelyn.raydium.RaydiumLiquidityPoolPositionConcentrated;
|
import com.fanitas.evelyn.raydium.RaydiumLiquidityPoolPositionConcentrated;
|
||||||
import com.fanitas.evelyn.core.DesiredPositionCalculator;
|
import com.fanitas.evelyn.core.DesiredPositionCalculator;
|
||||||
|
|
||||||
|
import com.r35157.libs.valuetypes.basic.AssetPrice;
|
||||||
import com.r35157.libs.valuetypes.basic.CurrencyType;
|
import com.r35157.libs.valuetypes.basic.CurrencyType;
|
||||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
@@ -27,37 +29,38 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, MoneyAmount> calculateRebalancingProposal(
|
public HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ>> calculateRebalancingProposal(
|
||||||
ΩPriceΩ currentPrice,
|
ΩSyrupPriceΩ currentPrice,
|
||||||
MoneyAmount totalReadyAmountMintA,
|
ΩSolanaAmountΩ totalReadyAmountMintA,
|
||||||
MoneyAmount totalReadyAmountMintB
|
ΩSyrupAmountΩ totalReadyAmountMintB
|
||||||
) {
|
) {
|
||||||
List<ΩPriceΩ> intervalStarts = getSortedPositionIntervalFromValues(liquidityProviderPositions);
|
List<ΩSyrupPriceΩ> intervalStarts = getSortedPositionIntervalFromValues(liquidityProviderPositions);
|
||||||
List<ΩPriceΩ> intervalEnds = getSortedPositionIntervalToValues(liquidityProviderPositions);
|
List<ΩSyrupPriceΩ> intervalEnds = getSortedPositionIntervalToValues(liquidityProviderPositions);
|
||||||
|
|
||||||
int indexOfPositionWithAnEndPriceBeforePositionWithCurrentPrice = lookupIndexOfLastLessThanOrEqual(
|
int indexOfPositionWithAnEndPriceBeforePositionWithCurrentPrice = lookupIndexOfLastLessThanOrEqual(
|
||||||
intervalStarts,
|
intervalStarts,
|
||||||
currentPrice
|
currentPrice
|
||||||
);
|
);
|
||||||
|
|
||||||
ΩPriceΩ lastLessThanOrEqual = intervalEnds.get(indexOfPositionWithAnEndPriceBeforePositionWithCurrentPrice);
|
ΩSyrupPriceΩ lastLessThanOrEqual = intervalEnds.get(indexOfPositionWithAnEndPriceBeforePositionWithCurrentPrice);
|
||||||
HashMap<ΩRaydiumLiquidityPoolPositionIdΩ, MoneyAmount> desiredPositions = new HashMap<>();
|
HashMap<ΩRaydiumLiquidityPoolPositionIdΩ, MoneyAmount> desiredPositions = new HashMap<>();
|
||||||
|
|
||||||
boolean beforeNonZeroPos = true;
|
boolean beforeNonZeroPos = true;
|
||||||
|
|
||||||
for (int i = 0; i < intervalStarts.size(); i++) {
|
for (int i = 0; i < intervalStarts.size(); i++) {
|
||||||
ΩPriceΩ iStart = intervalStarts.get(i);
|
ΩSyrupPriceΩ iStart = intervalStarts.get(i);
|
||||||
ΩPriceΩ iEnd = intervalEnds.get(i);
|
ΩSyrupPriceΩ iEnd = intervalEnds.get(i);
|
||||||
|
|
||||||
ΩAmountΩ dp = calculateDesiredPositionSizeForInterval(
|
ΩSolanaAmountΩ desiredSolanaPosSize = null; // TODO: Support this
|
||||||
|
ΩSyrupAmountΩ desiredSyrupPosSize = calculateDesiredPositionSizeForInterval(
|
||||||
currentPrice,
|
currentPrice,
|
||||||
totalReadyAmountMintB.amount(),
|
totalReadyAmountMintB,
|
||||||
iStart,
|
iStart,
|
||||||
iEnd,
|
iEnd,
|
||||||
lastLessThanOrEqual
|
lastLessThanOrEqual
|
||||||
);
|
);
|
||||||
|
|
||||||
int desiredPositionSign = dp.compareTo(ZERO);
|
int desiredPositionSign = desiredSyrupPosSize.amount().compareTo(ZERO);
|
||||||
|
|
||||||
if (desiredPositionSign > 0) {
|
if (desiredPositionSign > 0) {
|
||||||
beforeNonZeroPos = false;
|
beforeNonZeroPos = false;
|
||||||
@@ -65,21 +68,31 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
MoneyAmount desiredPositionSize = new MoneyAmount(
|
ΩSolanaAmountΩ desiredPositionSizeSolana = new MoneyAmount(
|
||||||
dp,
|
desiredSolanaPosSize,
|
||||||
|
totalReadyAmountMintA.currencyType()
|
||||||
|
);
|
||||||
|
|
||||||
|
ΩSyrupAmountΩ desiredPositionSizeSyrup = new MoneyAmount(
|
||||||
|
desiredSyrupPosSize,
|
||||||
totalReadyAmountMintB.currencyType()
|
totalReadyAmountMintB.currencyType()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> desiredPositionSizes =
|
||||||
|
new Pair<>(desiredPositionSizeSolana, desiredPositionSizeSyrup);
|
||||||
|
|
||||||
RaydiumLiquidityPoolPositionConcentrated position = getPositionByStartPriceA(iStart);
|
RaydiumLiquidityPoolPositionConcentrated position = getPositionByStartPriceA(iStart);
|
||||||
|
// TODO: What if null is return above?
|
||||||
ΩRaydiumLiquidityPoolPositionNftIdΩ poolPositionId = position.nftId();
|
ΩRaydiumLiquidityPoolPositionNftIdΩ poolPositionId = position.nftId();
|
||||||
desiredPositions.put(poolPositionId, desiredPositionSize);
|
|
||||||
|
desiredPositions.put(poolPositionId, desiredPositionSizes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return desiredPositions;
|
return desiredPositions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<MoneyAmount, MoneyAmount> calculateTotalDistributedSums() {
|
public Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> calculateTotalDistributedSums() {
|
||||||
Collection<RaydiumLiquidityPoolPositionConcentrated> c = liquidityProviderPositions.values();
|
Collection<RaydiumLiquidityPoolPositionConcentrated> c = liquidityProviderPositions.values();
|
||||||
|
|
||||||
if(c.isEmpty()) {
|
if(c.isEmpty()) {
|
||||||
@@ -107,7 +120,7 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<MoneyAmount, MoneyAmount> calculateLockedSums(ΩPriceΩ currentPrice) {
|
public Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> calculateLockedSums(ΩSyrupPriceΩ currentPrice) {
|
||||||
List<ΩPriceΩ> ascendingPrices = getSortedPositionIntervalFromValues(liquidityProviderPositions);
|
List<ΩPriceΩ> ascendingPrices = getSortedPositionIntervalFromValues(liquidityProviderPositions);
|
||||||
ΩAmountΩ sumA = ZERO;
|
ΩAmountΩ sumA = ZERO;
|
||||||
ΩAmountΩ sumB = ZERO;
|
ΩAmountΩ sumB = ZERO;
|
||||||
@@ -116,7 +129,7 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
CurrencyType ctA = null;
|
CurrencyType ctA = null;
|
||||||
CurrencyType ctB = null;
|
CurrencyType ctB = null;
|
||||||
|
|
||||||
int index = lookupIndexOfFirstGreaterThanOrEqual(ascendingPrices, currentPrice);
|
int index = lookupIndexOfFirstGreaterThanOrEqual(ascendingPrices, currentPrice.price());
|
||||||
if(index >= 0) {
|
if(index >= 0) {
|
||||||
ΩPriceΩ startPriceOfStartPos = ascendingPrices.get(index);
|
ΩPriceΩ startPriceOfStartPos = ascendingPrices.get(index);
|
||||||
|
|
||||||
@@ -137,12 +150,12 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Pair<MoneyAmount, MoneyAmount> calculateRedistributableSums(
|
public Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> calculateRedistributableSums(
|
||||||
ΩPriceΩ currentPrice,
|
ΩSyrupPriceΩ currentPrice,
|
||||||
MoneyAmount inactiveInAccountMintA,
|
ΩSolanaAmountΩ inactiveInAccountMintA,
|
||||||
MoneyAmount inactiveInAccountMintB,
|
ΩSolanaAmountΩ reservedForBurnMintA,
|
||||||
MoneyAmount reservedForBurnMintA,
|
ΩSyrupAmountΩ inactiveInAccountMintB,
|
||||||
MoneyAmount reservedForBurnMintB
|
ΩSyrupAmountΩ reservedForBurnMintB
|
||||||
) {
|
) {
|
||||||
List<ΩPriceΩ> ascendingValues = getSortedPositionIntervalFromValues(liquidityProviderPositions);
|
List<ΩPriceΩ> ascendingValues = getSortedPositionIntervalFromValues(liquidityProviderPositions);
|
||||||
ΩAmountΩ redistSumA = ZERO;
|
ΩAmountΩ redistSumA = ZERO;
|
||||||
@@ -150,7 +163,7 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
CurrencyType ctA = null;
|
CurrencyType ctA = null;
|
||||||
CurrencyType ctB = null;
|
CurrencyType ctB = null;
|
||||||
|
|
||||||
int index = lookupIndexOfLastLessThan(ascendingValues, currentPrice);
|
int index = lookupIndexOfLastLessThan(ascendingValues, currentPrice.price());
|
||||||
if(index >= 0) {
|
if(index >= 0) {
|
||||||
ΩPriceΩ startPriceOfStartPos = ascendingValues.get(index);
|
ΩPriceΩ startPriceOfStartPos = ascendingValues.get(index);
|
||||||
|
|
||||||
@@ -174,8 +187,6 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
return new Pair<>(ma, mb);
|
return new Pair<>(ma, mb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: Can this be deleted?
|
|
||||||
private MoneyAmount add(ΩAmountΩ a, ΩAmountΩ b, CurrencyType ct) {
|
private MoneyAmount add(ΩAmountΩ a, ΩAmountΩ b, CurrencyType ct) {
|
||||||
MoneyAmount ma = new MoneyAmount(a.add(b), ct);
|
MoneyAmount ma = new MoneyAmount(a.add(b), ct);
|
||||||
return ma;
|
return ma;
|
||||||
@@ -194,7 +205,7 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
return a.subtract(b.amount());
|
return a.subtract(b.amount());
|
||||||
}
|
}
|
||||||
|
|
||||||
private RaydiumLiquidityPoolPositionConcentrated getPositionByStartPriceA(ΩPriceΩ startPriceA) {
|
private RaydiumLiquidityPoolPositionConcentrated getPositionByStartPriceA(@NotNull ΩPriceΩ startPriceA) {
|
||||||
for (RaydiumLiquidityPoolPositionConcentrated candidate : liquidityProviderPositions.values()) {
|
for (RaydiumLiquidityPoolPositionConcentrated candidate : liquidityProviderPositions.values()) {
|
||||||
if (candidate.priceRange().from().compareTo(startPriceA) == 0) {
|
if (candidate.priceRange().from().compareTo(startPriceA) == 0) {
|
||||||
return candidate;
|
return candidate;
|
||||||
@@ -204,12 +215,12 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ΩAmountΩ calculateDesiredPositionSizeForInterval(
|
private ΩSyrupAmountΩ calculateDesiredPositionSizeForInterval(
|
||||||
ΩPriceΩ currentPrice,
|
@NotNull ΩSyrupPriceΩ currentPrice,
|
||||||
ΩAmountΩ totalSyrupToDistribution,
|
@NotNull ΩSolanaAmountΩ totalSyrupToDistribution,
|
||||||
ΩPriceΩ intervalPriceFrom,
|
@NotNull ΩSyrupPriceΩ intervalPriceFrom,
|
||||||
ΩPriceΩ intervalPriceTo,
|
@NotNull ΩSyrupPriceΩ intervalPriceTo,
|
||||||
ΩPriceΩ lookupPrice
|
@NotNull ΩSyrupPriceΩ lookupPrice
|
||||||
) {
|
) {
|
||||||
ΩPriceΩ curveStartPrice = currentPrice.multiply(
|
ΩPriceΩ curveStartPrice = currentPrice.multiply(
|
||||||
ONE.subtract(curveWidth, MC),
|
ONE.subtract(curveWidth, MC),
|
||||||
@@ -256,97 +267,109 @@ public class DesiredPositionCalculatorImpl implements DesiredPositionCalculator
|
|||||||
return dps;
|
return dps;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int lookupIndexOfFirstGreaterThan(List<ΩPriceΩ> ascendingPrices, ΩPriceΩ searchPrice) {
|
private static int lookupIndexOfFirstGreaterThan(
|
||||||
int index = -1;
|
@NotNull List<ΩSyrupPriceΩ> ascendingPrices,
|
||||||
|
@NotNull ΩSyrupPriceΩ searchPrice
|
||||||
for (int i = ascendingPrices.size() - 1; i >= 0; i--) {
|
|
||||||
BigDecimal p = ascendingPrices.get(i);
|
|
||||||
if (p.compareTo(searchPrice) <= 0) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == -1) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"No position has a greater start price than a position containing '" + searchPrice + "'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int lookupIndexOfFirstGreaterThanOrEqual(List<ΩPriceΩ> ascendingPrices, ΩPriceΩ searchPrice) {
|
|
||||||
int index = -1;
|
|
||||||
|
|
||||||
for (int i = ascendingPrices.size() - 1; i >= 0; i--) {
|
|
||||||
BigDecimal p = ascendingPrices.get(i);
|
|
||||||
if (p.compareTo(searchPrice) <= 0) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
index = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == -1) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"No position has a greater start price than a position containing '" + searchPrice + "'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int lookupIndexOfLastLessThanOrEqual(List<ΩPriceΩ> ascendingPrices, ΩPriceΩ searchPrice) {
|
|
||||||
int index = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < ascendingPrices.size() - 1; i++) {
|
|
||||||
ΩPriceΩ p = ascendingPrices.get(i);
|
|
||||||
if (p.compareTo(searchPrice) < 0) {
|
|
||||||
index = i - 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int lookupIndexOfLastLessThan(List<ΩPriceΩ> ascendingPrices, ΩPriceΩ searchPrice) {
|
|
||||||
int index = -1;
|
|
||||||
|
|
||||||
for (int i = 0; i < ascendingPrices.size() - 1; i++) {
|
|
||||||
ΩPriceΩ p = ascendingPrices.get(i);
|
|
||||||
if (p.compareTo(searchPrice) < 0) {
|
|
||||||
index = i - 1;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<ΩPriceΩ> getSortedPositionIntervalFromValues(
|
|
||||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityPositions
|
|
||||||
) {
|
) {
|
||||||
List<ΩPriceΩ> result = new ArrayList<>(liquidityPositions.size());
|
int index = -1;
|
||||||
|
|
||||||
|
for (int i = ascendingPrices.size() - 1; i >= 0; i--) {
|
||||||
|
BigDecimal p = ascendingPrices.get(i);
|
||||||
|
if (p.compareTo(searchPrice) <= 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"No position has a greater start price than a position containing '" + searchPrice + "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int lookupIndexOfFirstGreaterThanOrEqual(
|
||||||
|
@NotNull List<ΩSyrupPriceΩ> ascendingPrices,
|
||||||
|
@NotNull ΩSyrupPriceΩ searchPrice
|
||||||
|
) {
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
for (int i = ascendingPrices.size() - 1; i >= 0; i--) {
|
||||||
|
BigDecimal p = ascendingPrices.get(i);
|
||||||
|
if (p.compareTo(searchPrice) <= 0) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == -1) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"No position has a greater start price than a position containing '" + searchPrice + "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int lookupIndexOfLastLessThanOrEqual(
|
||||||
|
@NotNull List<ΩSyrupPriceΩ> ascendingPrices,
|
||||||
|
@NotNull ΩSyrupPriceΩ searchPrice
|
||||||
|
) {
|
||||||
|
int index = -1;
|
||||||
|
for (int i = 0; i < ascendingPrices.size() - 1; i++) {
|
||||||
|
ΩSyrupPriceΩ p = ascendingPrices.get(i);
|
||||||
|
if (p.compareTo(searchPrice) < 0) {
|
||||||
|
index = i - 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int lookupIndexOfLastLessThan(
|
||||||
|
@NotNull List<ΩSyrupPriceΩ> ascendingPrices,
|
||||||
|
@NotNull ΩSyrupPriceΩ searchPrice
|
||||||
|
) {
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
for (int i = 0; i < ascendingPrices.size() - 1; i++) {
|
||||||
|
ΩPriceΩ p = ascendingPrices.get(i);
|
||||||
|
if (p.compareTo(searchPrice) < 0) {
|
||||||
|
index = i - 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull List<ΩSyrupPriceΩ> getSortedPositionIntervalFromValues(
|
||||||
|
@NotNull Map<ΩRaydiumLiquidityPoolPositionNftIdΩ,
|
||||||
|
@NotNull RaydiumLiquidityPoolPositionConcentrated> liquidityPositions
|
||||||
|
) {
|
||||||
|
List<ΩSyrupPriceΩ> result = new ArrayList<>(liquidityPositions.size());
|
||||||
for (RaydiumLiquidityPoolPositionConcentrated position : liquidityPositions.values()) {
|
for (RaydiumLiquidityPoolPositionConcentrated position : liquidityPositions.values()) {
|
||||||
result.add(position.priceRange().from());
|
result.add(position.priceRange().from());
|
||||||
}
|
}
|
||||||
result.sort(ΩPriceΩ::compareTo);
|
result.sort(ΩSyrupPriceΩ::compareTo);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ΩPriceΩ> getSortedPositionIntervalToValues(
|
private static @NotNull List<ΩSyrupPriceΩ> getSortedPositionIntervalToValues(
|
||||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityPositions
|
@NotNull Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityPositions
|
||||||
) {
|
) {
|
||||||
List<ΩPriceΩ> result = new ArrayList<>(liquidityPositions.size());
|
List<ΩSyrupPriceΩ> result = new ArrayList<>(liquidityPositions.size());
|
||||||
for (RaydiumLiquidityPoolPositionConcentrated position : liquidityPositions.values()) {
|
for (RaydiumLiquidityPoolPositionConcentrated position : liquidityPositions.values()) {
|
||||||
result.add(position.priceRange().to());
|
result.add(position.priceRange().to());
|
||||||
}
|
}
|
||||||
result.sort(ΩPriceΩ::compareTo);
|
result.sort(ΩSyrupPriceΩ::compareTo);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import com.r35157.libs.raydium.RaydiumLiquidityPoolPrice;
|
|||||||
import com.r35157.libs.solana.SPLTokenHolding;
|
import com.r35157.libs.solana.SPLTokenHolding;
|
||||||
import com.r35157.libs.solana.SolanaBlockChain;
|
import com.r35157.libs.solana.SolanaBlockChain;
|
||||||
import com.r35157.libs.solana.SolanaConstants;
|
import com.r35157.libs.solana.SolanaConstants;
|
||||||
|
import com.r35157.libs.solana.valuetypes.WellKnownCurrencyTypes;
|
||||||
|
import com.r35157.libs.valuetypes.basic.AssetPrice;
|
||||||
|
import com.r35157.libs.valuetypes.basic.CurrencyType;
|
||||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
||||||
|
import com.r35157.libs.valuetypes.basic.TradingPair;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@@ -18,6 +22,8 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
|
import static com.r35157.libs.solana.valuetypes.WellKnownCurrencyTypes.SOLANA;
|
||||||
|
import static com.r35157.libs.solana.valuetypes.WellKnownCurrencyTypes.SYRUPUSDC;
|
||||||
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.SPL_TOKEN_PROGRAM;
|
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.SPL_TOKEN_PROGRAM;
|
||||||
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.TOKEN_2022_PROGRAM;
|
import static com.r35157.libs.solana.valuetypes.economic.SolanaSPLTokenProgram.TOKEN_2022_PROGRAM;
|
||||||
|
|
||||||
@@ -55,10 +61,21 @@ public class EvelynImpl implements Evelyn {
|
|||||||
return ma;
|
return ma;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MoneyAmount add(MoneyAmount a, MoneyAmount b) {
|
||||||
|
if(a.currencyType() != b.currencyType()) {
|
||||||
|
String errTxt = "Cannot add " + b.currencyType()
|
||||||
|
+ " to " + a.currencyType();
|
||||||
|
throw new IllegalArgumentException(errTxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
MoneyAmount ma = new MoneyAmount(a.amount().add(b.amount()), a.currencyType());
|
||||||
|
return ma;
|
||||||
|
}
|
||||||
|
|
||||||
private MoneyAmount subtract(MoneyAmount a, MoneyAmount b) {
|
private MoneyAmount subtract(MoneyAmount a, MoneyAmount b) {
|
||||||
if(a.currencyType() != b.currencyType()) {
|
if(a.currencyType() != b.currencyType()) {
|
||||||
String errTxt = "Cannot subtract " + b.currencyType().name()
|
String errTxt = "Cannot subtract " + b.currencyType()
|
||||||
+ " from " + a.currencyType().name();
|
+ " from " + a.currencyType();
|
||||||
throw new IllegalArgumentException(errTxt);
|
throw new IllegalArgumentException(errTxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,16 +97,18 @@ public class EvelynImpl implements Evelyn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleRebalancingProposal() {
|
private void handleRebalancingProposal() {
|
||||||
HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, MoneyAmount> rebalancingProposal;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ΩSolanaAddressΩ solanaAddressForEvelynIOU = state.getSolanaAddressForEvelynIOU();
|
ΩSolanaAddressΩ solanaAddressForEvelynIOU = state.getSolanaAddressForEvelynIOU();
|
||||||
ΩSPLMintAddressΩ syrupUSDCMintAddr = SolanaConstants.SPL_TOKEN_SYRUPUSDC;
|
ΩSPLMintAddressΩ syrupUSDCMintAddr = SolanaConstants.SPL_TOKEN_SYRUPUSDC;
|
||||||
|
|
||||||
|
AssetPrice currentPriceFromRaydium;
|
||||||
|
ΩSyrupPriceΩ currentPriceFromChain;
|
||||||
SPLTokenHolding syrupHolding = getSPLHolding(solanaAddressForEvelynIOU, syrupUSDCMintAddr);
|
SPLTokenHolding syrupHolding = getSPLHolding(solanaAddressForEvelynIOU, syrupUSDCMintAddr);
|
||||||
ΩAmountΩ inactiveInAccountSyrup = syrupHolding.uiAmount();
|
|
||||||
ΩPriceΩ currentPriceFromRaydium;
|
ΩSyrupAmountΩ inactiveInAccountSyrup = new ΩSyrupAmountΩ(
|
||||||
MoneyAmount currentPriceFromChain;
|
syrupHolding.uiAmount(),
|
||||||
|
SYRUPUSDC.getCurrencyType()
|
||||||
|
);
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
state.update();
|
state.update();
|
||||||
@@ -105,21 +124,27 @@ public class EvelynImpl implements Evelyn {
|
|||||||
);
|
);
|
||||||
|
|
||||||
concentratedResult = cFuture.get();
|
concentratedResult = cFuture.get();
|
||||||
currentPriceFromRaydium = concentratedResult.amount().amount();
|
|
||||||
|
currentPriceFromRaydium = new AssetPrice(
|
||||||
|
concentratedResult.price(),
|
||||||
|
tpSolSyrup
|
||||||
|
);
|
||||||
}
|
}
|
||||||
currentPriceFromChain = raydium.fetchPoolPrice(state.getRaydiumPoolId());
|
|
||||||
|
ΩPriceΩ price = raydium.fetchPoolPrice(state.getRaydiumPoolId());
|
||||||
|
currentPriceFromChain = new AssetPrice(price, tpSolSyrup);
|
||||||
|
|
||||||
System.out.println("Iteration interval: " + (state.getIterationInterval() / 1000) + " secs");
|
System.out.println("Iteration interval: " + (state.getIterationInterval() / 1000) + " secs");
|
||||||
|
|
||||||
System.out.println("Solana balances:");
|
System.out.println("Solana balances:");
|
||||||
ΩSolanaAmountΩ solBalanceEvelyn = solanaChain.getBalanceInSolana(state.getSolanaAddressForEvelynIOU());
|
ΩSolanaAmountΩ solBalanceEvelynAccount = solanaChain.getBalanceInSolana(state.getSolanaAddressForEvelynIOU());
|
||||||
System.out.println(" Evelyn: " + solBalanceEvelyn.amount());
|
System.out.println(" Evelyn account: " + solBalanceEvelynAccount.amount());
|
||||||
ΩSolanaAmountΩ solBalanceBurn = solanaChain.getBalanceInSolana(state.getSolanaAddressForEvelynIOUBurner());
|
ΩSolanaAmountΩ solBalanceBurnAccount = solanaChain.getBalanceInSolana(state.getSolanaAddressForEvelynIOUBurner());
|
||||||
System.out.println(" Burn: " + solBalanceBurn.amount());
|
System.out.println(" Burn account: " + solBalanceBurnAccount.amount());
|
||||||
System.out.println("SYRUP owned by Evelyn: " + state.getSyrupOwnedByEvelyn().amount());
|
System.out.println("SYRUP owned by Evelyn: " + state.getSyrupOwnedByEvelyn().amount());
|
||||||
System.out.println("SYRUP inactive on Evelyn account: " + inactiveInAccountSyrup);
|
System.out.println("SYRUP inactive on Evelyn account: " + inactiveInAccountSyrup);
|
||||||
|
|
||||||
System.out.println("Pool price: " + currentPriceFromRaydium + "/" + currentPriceFromChain.amount());
|
System.out.println("Pool price: " + currentPriceFromRaydium + "/" + currentPriceFromChain);
|
||||||
|
|
||||||
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> totalDistributedSums =
|
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> totalDistributedSums =
|
||||||
desiredPositionCalculator.calculateTotalDistributedSums();
|
desiredPositionCalculator.calculateTotalDistributedSums();
|
||||||
@@ -129,7 +154,7 @@ public class EvelynImpl implements Evelyn {
|
|||||||
+ " / " + totalDistributedSumSyrup);
|
+ " / " + totalDistributedSumSyrup);
|
||||||
|
|
||||||
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> amountsLocked =
|
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> amountsLocked =
|
||||||
desiredPositionCalculator.calculateLockedSums(currentPriceFromChain.amount());
|
desiredPositionCalculator.calculateLockedSums(currentPriceFromChain);
|
||||||
ΩSolanaAmountΩ amountLockedSolana = amountsLocked.left();
|
ΩSolanaAmountΩ amountLockedSolana = amountsLocked.left();
|
||||||
ΩSyrupAmountΩ amountLockedSyrup = amountsLocked.right();
|
ΩSyrupAmountΩ amountLockedSyrup = amountsLocked.right();
|
||||||
System.out.println("Total amount locked due to HIGH price: " + amountLockedSolana);
|
System.out.println("Total amount locked due to HIGH price: " + amountLockedSolana);
|
||||||
@@ -138,29 +163,35 @@ public class EvelynImpl implements Evelyn {
|
|||||||
ΩSyrupAmountΩ totalAmountSyrup = add(totalDistributedSumSyrup, inactiveInAccountSyrup);
|
ΩSyrupAmountΩ totalAmountSyrup = add(totalDistributedSumSyrup, inactiveInAccountSyrup);
|
||||||
ΩSyrupAmountΩ reservedForBurnSyrup = subtract(totalAmountSyrup, state.getSyrupOwnedByEvelyn());
|
ΩSyrupAmountΩ reservedForBurnSyrup = subtract(totalAmountSyrup, state.getSyrupOwnedByEvelyn());
|
||||||
|
|
||||||
ΩSolanaAmountΩ readyForBurnSolana = subtract(solBalanceEvelyn, SOFT_LOW_LIMIT_SOLANA_BALANCE);
|
ΩSolanaAmountΩ readyForBurnSolana = subtract(solBalanceEvelynAccount, SOFT_LOW_LIMIT_SOLANA_BALANCE);
|
||||||
System.out.println("Amount reserved for burn: "
|
System.out.println("Amount reserved for burn: "
|
||||||
+ "Solana: " + readyForBurnSolana
|
+ "Solana: " + readyForBurnSolana
|
||||||
+ ", Syrup:" + reservedForBurnSyrup);
|
+ ", Syrup:" + reservedForBurnSyrup);
|
||||||
|
|
||||||
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> syrupTotalReadyAmount = desiredPositionCalculator.calculateRedistributableSums(
|
Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ> totalReadyAmount = desiredPositionCalculator
|
||||||
currentPriceFromChain.amount(),
|
.calculateRedistributableSums(
|
||||||
new ΩSyrupAmountΩ(inactiveInAccountSyrup, readyForBurnSolana.currencyType()), // TODO: Wow! This is not pretty! Stealing cyrrenctType from another object. Oh dear!
|
currentPriceFromChain,
|
||||||
reservedForBurnSyrup
|
solBalanceEvelynAccount,
|
||||||
);
|
amountZeroSolana, // We do not burn Solana
|
||||||
System.out.println("Total amount of Syrup ready for distribution: " + syrupTotalReadyAmount.amount());
|
inactiveInAccountSyrup,
|
||||||
|
reservedForBurnSyrup
|
||||||
|
);
|
||||||
|
ΩSolanaAmountΩ solTotalReadyAmount = totalReadyAmount.left();
|
||||||
|
ΩSyrupAmountΩ syrupTotalReadyAmount = totalReadyAmount.right();
|
||||||
|
System.out.println("Total amount of Syrup ready for distribution: " + syrupTotalReadyAmount);
|
||||||
|
|
||||||
rebalancingProposal = desiredPositionCalculator.calculateRebalancingProposal(
|
HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ>> rebalancingProposal =
|
||||||
currentPriceFromChain.amount(),
|
desiredPositionCalculator.calculateRebalancingProposal(
|
||||||
syrupTotalReadyAmount
|
currentPriceFromChain,
|
||||||
);
|
solTotalReadyAmount,
|
||||||
|
syrupTotalReadyAmount
|
||||||
|
);
|
||||||
|
|
||||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityPositions = state.getLiquidityPositions();
|
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityPositions =
|
||||||
|
state.getLiquidityPositions();
|
||||||
|
|
||||||
Map<String, Triblet> diffs = calculateDiffs(
|
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, Triblet<ΩSyrupAmountΩ, ΩSyrupAmountΩ, ΩSyrupAmountΩ>> diffs =
|
||||||
rebalancingProposal,
|
calculateDiffs(rebalancingProposal, liquidityPositions);
|
||||||
liquidityPositions
|
|
||||||
);
|
|
||||||
|
|
||||||
String minKey = diffs.entrySet().stream()
|
String minKey = diffs.entrySet().stream()
|
||||||
.min(Comparator.comparing(entry -> entry.getValue().diff()))
|
.min(Comparator.comparing(entry -> entry.getValue().diff()))
|
||||||
@@ -205,24 +236,39 @@ public class EvelynImpl implements Evelyn {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private HashMap<String, Triblet> calculateDiffs(
|
private HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, Triblet<ΩSyrupAmountΩ, ΩSyrupAmountΩ, ΩSyrupAmountΩ>> calculateDiffs(
|
||||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, ΩSyrupAmountΩ> rebalancingProposal,
|
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, Pair<ΩSolanaAmountΩ, ΩSyrupAmountΩ>> rebalancingProposal,
|
||||||
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityProviderPositionMap
|
Map<ΩRaydiumLiquidityPoolPositionNftIdΩ, RaydiumLiquidityPoolPositionConcentrated> liquidityProviderPositionMap
|
||||||
) {
|
) {
|
||||||
HashMap<String, Triblet> diffs = new HashMap<>(rebalancingProposal.size());
|
HashMap<ΩRaydiumLiquidityPoolPositionNftIdΩ, Triblet<ΩSyrupAmountΩ, ΩSyrupAmountΩ, ΩSyrupAmountΩ>> diffs =
|
||||||
|
new HashMap<>(rebalancingProposal.size());
|
||||||
|
|
||||||
for(String nftId : rebalancingProposal.keySet()) {
|
for(ΩRaydiumLiquidityPoolPositionNftIdΩ nftId : rebalancingProposal.keySet()) {
|
||||||
RaydiumLiquidityPoolPositionConcentrated pos = liquidityProviderPositionMap.get(nftId);
|
RaydiumLiquidityPoolPositionConcentrated pos = liquidityProviderPositionMap.get(nftId);
|
||||||
ΩAmountΩ currentlyAmountAdded = pos.amountMintB().amount();
|
ΩAmountΩ currentlyAmountAdded = pos.amountMintB().amount();
|
||||||
ΩSyrupAmountΩ suggestedAmount = rebalancingProposal.get(nftId);
|
ΩSyrupAmountΩ suggestedAmount = rebalancingProposal.get(nftId);
|
||||||
ΩAmountΩ diff = subtract(currentlyAmountAdded, suggestedAmount);
|
ΩAmountΩ diff = subtract(currentlyAmountAdded, suggestedAmount);
|
||||||
Triblet t = new Triblet(currentlyAmountAdded, suggestedAmount.amount(), diff);
|
Triblet<ΩSyrupAmountΩ, ΩSyrupAmountΩ, ΩSyrupAmountΩ> t = new Triblet<>(
|
||||||
|
currentlyAmountAdded,
|
||||||
|
suggestedAmount.amount(),
|
||||||
|
diff
|
||||||
|
);
|
||||||
diffs.put(nftId, t);
|
diffs.put(nftId, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
return diffs;
|
return diffs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final static TradingPair tpSolSyrup = new TradingPair(
|
||||||
|
SOLANA.getCurrencyType(),
|
||||||
|
SYRUPUSDC.getCurrencyType()
|
||||||
|
);
|
||||||
|
|
||||||
|
private final static ΩSolanaAmountΩ amountZeroSolana = new ΩSolanaAmountΩ(
|
||||||
|
BigDecimal.ZERO,
|
||||||
|
SOLANA.getCurrencyType()
|
||||||
|
);
|
||||||
|
|
||||||
private final ΩAmountΩ SOFT_LOW_LIMIT_SOLANA_BALANCE = new BigDecimal("0.1");
|
private final ΩAmountΩ SOFT_LOW_LIMIT_SOLANA_BALANCE = new BigDecimal("0.1");
|
||||||
|
|
||||||
private final State state;
|
private final State state;
|
||||||
|
|||||||
+7
-6
@@ -1,6 +1,7 @@
|
|||||||
package com.fanitas.evelyn.raydium;
|
package com.fanitas.evelyn.raydium;
|
||||||
|
|
||||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
@@ -21,11 +22,11 @@ import java.math.BigDecimal;
|
|||||||
* @param accountingInfo the amount added to and borrowed from the position
|
* @param accountingInfo the amount added to and borrowed from the position
|
||||||
*/
|
*/
|
||||||
public record RaydiumLiquidityPoolPositionConcentrated(
|
public record RaydiumLiquidityPoolPositionConcentrated(
|
||||||
ΩRaydiumLiquidityPoolConcentratedIdΩ poolId,
|
@NotNull ΩRaydiumLiquidityPoolConcentratedIdΩ poolId,
|
||||||
ΩRaydiumLiquidityPoolPositionNftIdΩ nftId,
|
@NotNull ΩRaydiumLiquidityPoolPositionNftIdΩ nftId,
|
||||||
PriceRange priceRange,
|
@NotNull PriceRange priceRange,
|
||||||
MoneyAmount amountMintA,
|
@NotNull MoneyAmount amountMintA,
|
||||||
MoneyAmount amountMintB,
|
@NotNull MoneyAmount amountMintB,
|
||||||
RaydiumLiquidityPoolPositionAccounting accountingInfo
|
@NotNull RaydiumLiquidityPoolPositionAccounting accountingInfo
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
package com.r35157.libs.raydium;
|
package com.r35157.libs.raydium;
|
||||||
|
|
||||||
import com.r35157.libs.valuetypes.basic.MoneyAmount;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the price of a Raydium liquidity pool.
|
* Represents the current price of the tokens in a Raydium liquidity pool.
|
||||||
*
|
*
|
||||||
* <p>The pool id identifies the Raydium liquidity pool for which the price applies.
|
* <p>The 'poolId' identifies the Raydium liquidity pool for which the current token price applies.
|
||||||
* The amount contains the price value returned or calculated for that pool.</p>
|
* The 'price' represents the price value returned or calculated for that pool.</p>
|
||||||
*
|
*
|
||||||
* @param poolId the Raydium liquidity pool id that the price belongs to
|
* @param poolId the Raydium liquidity pool id that the price belongs to
|
||||||
* @param amount the price amount for the liquidity pool
|
* @param price the price for a token in the liquidity pool
|
||||||
*/
|
*/
|
||||||
public record RaydiumLiquidityPoolPrice(
|
public record RaydiumLiquidityPoolPrice(
|
||||||
ΩRaydiumLiquidityPoolIdΩ poolId,
|
ΩRaydiumLiquidityPoolIdΩ poolId,
|
||||||
MoneyAmount amount
|
ΩPriceΩ price
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@@ -5,11 +5,24 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
public record AssetPrice(
|
public record AssetPrice(
|
||||||
ΩPriceΩ price,
|
@NotNull ΩPriceΩ price,
|
||||||
CurrencyType currencyType
|
@NotNull TradingPair tradingPair
|
||||||
) {
|
) implements Comparable<AssetPrice> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(@NotNull AssetPrice other) {
|
||||||
|
if (!tradingPair.equals(other.tradingPair)) {
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"AssetPrice values with different trading pairs cannot be compared: "
|
||||||
|
+ tradingPair + " and " + other.tradingPair
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return price.compareTo(other.price);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NotNull String toString() {
|
public @NotNull String toString() {
|
||||||
return price + " " + currencyType();
|
return price + " " + tradingPair.quote();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
package com.r35157.libs.valuetypes.basic;
|
package com.r35157.libs.valuetypes.basic;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
public record TradingPair(
|
public record TradingPair(
|
||||||
CurrencyType base, // The thing you are buying or selling.
|
CurrencyType base, // The thing you are buying or selling.
|
||||||
CurrencyType quote // The currency/unit used to price the base asset.
|
CurrencyType quote // The currency/unit used to price the base asset.
|
||||||
) { }
|
) {
|
||||||
|
@Override
|
||||||
|
public @NotNull String toString() {
|
||||||
|
return base.symbol() + "_" + quote.symbol();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user