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