ScriptUtil脚本实现
This commit is contained in:
parent
3bac0e4aee
commit
8ac9a8ec1a
15
pom.xml
15
pom.xml
@ -5,12 +5,13 @@
|
|||||||
|
|
||||||
<groupId>com.sample</groupId>
|
<groupId>com.sample</groupId>
|
||||||
<artifactId>trend-grid-strategy</artifactId>
|
<artifactId>trend-grid-strategy</artifactId>
|
||||||
<version>1.0.2-SNAPSHOT</version>
|
<version>1.0.3-SNAPSHOT</version>
|
||||||
<name>trend-grid-strategy</name>
|
<name>trend-grid-strategy</name>
|
||||||
<description>趋势网格策略</description>
|
<description>趋势网格策略</description>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<trade.version>5.0.0-SNAPSHOT</trade.version>
|
<trade.version>5.0.0-SNAPSHOT</trade.version>
|
||||||
|
<graalvm.version>24.1.2</graalvm.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -24,6 +25,18 @@
|
|||||||
<artifactId>uuquant-exchange-api</artifactId>
|
<artifactId>uuquant-exchange-api</artifactId>
|
||||||
<version>20260619.0130</version>
|
<version>20260619.0130</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.polyglot</groupId>
|
||||||
|
<artifactId>polyglot</artifactId>
|
||||||
|
<version>${graalvm.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.graalvm.polyglot</groupId>
|
||||||
|
<artifactId>js</artifactId>
|
||||||
|
<version>${graalvm.version}</version>
|
||||||
|
<type>pom</type>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|||||||
@ -2,21 +2,31 @@ package com.sample.trend;
|
|||||||
|
|
||||||
|
|
||||||
import com.sample.trend.strategy.AutoGridStrategy;
|
import com.sample.trend.strategy.AutoGridStrategy;
|
||||||
|
import com.sample.trend.strategy.util.ScriptUtil;
|
||||||
import vip.uuquant.tradesystem.runner.core.StrategyRunner;
|
import vip.uuquant.tradesystem.runner.core.StrategyRunner;
|
||||||
import vip.uuquant.tradesystem.runner.vo.DebugConfig;
|
import vip.uuquant.tradesystem.runner.vo.DebugConfig;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class StrategyApplication {
|
public class StrategyApplication {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// 调试模式
|
// 调试模式
|
||||||
DebugConfig debugConfig = new DebugConfig();
|
// DebugConfig debugConfig = new DebugConfig();
|
||||||
debugConfig.setAgentHost("192.168.0.105");
|
// debugConfig.setAgentHost("192.168.0.105");
|
||||||
debugConfig.setRobotId(2074501096701927426L);
|
// debugConfig.setRobotId(2074501096701927426L);
|
||||||
debugConfig.setRobotApiKey("be7520970034b54cb7f2bccab0d546ca");
|
// debugConfig.setRobotApiKey("be7520970034b54cb7f2bccab0d546ca");
|
||||||
|
//
|
||||||
|
// StrategyRunner strategyRunner = StrategyRunner.getInstance();
|
||||||
|
// strategyRunner.setDebugConfig(debugConfig);
|
||||||
|
// strategyRunner.init(AutoGridStrategy.class);
|
||||||
|
|
||||||
StrategyRunner strategyRunner = StrategyRunner.getInstance();
|
Map<String, Object> map = new HashMap<>();
|
||||||
strategyRunner.setDebugConfig(debugConfig);
|
map.put("atr", 1);
|
||||||
strategyRunner.init(AutoGridStrategy.class);
|
BigDecimal a = ScriptUtil.evalBigDecimal("atr > 1 ? 2 : 5", map);
|
||||||
|
System.out.println(a);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject;
|
|||||||
import com.hengyi.xbaseweb.common.result.ApiResult;
|
import com.hengyi.xbaseweb.common.result.ApiResult;
|
||||||
import com.sample.trend.config.CommonConstant;
|
import com.sample.trend.config.CommonConstant;
|
||||||
import com.sample.trend.strategy.util.AtrCalculator;
|
import com.sample.trend.strategy.util.AtrCalculator;
|
||||||
|
import com.sample.trend.strategy.util.ScriptUtil;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -52,6 +53,7 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
// 运行参数
|
// 运行参数
|
||||||
private BigDecimal baseQuantity;
|
private BigDecimal baseQuantity;
|
||||||
private BigDecimal atrMultiplier;
|
private BigDecimal atrMultiplier;
|
||||||
|
private String gridGrpScript;
|
||||||
private String klineInterval;
|
private String klineInterval;
|
||||||
private int klinePeriod;
|
private int klinePeriod;
|
||||||
private int gridAdjustIntervalMinutes;
|
private int gridAdjustIntervalMinutes;
|
||||||
@ -259,12 +261,13 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
super.init(context);
|
super.init(context);
|
||||||
logger.info("ATR 动态网格策略初始化");
|
logger.info("ATR 动态网格策略初始化");
|
||||||
|
|
||||||
tryParams("base_quantity", "atr_multiplier", "kline_interval", "kline_period",
|
tryParams("base_quantity", "atr_multiplier", "grid_grp_script", "kline_interval", "kline_period",
|
||||||
"grid_adjust_interval", "min_gap", "max_gap", "upper_bound", "lower_bound",
|
"grid_adjust_interval", "min_gap", "max_gap", "upper_bound", "lower_bound",
|
||||||
"grid_count", "test_mode");
|
"grid_count", "test_mode");
|
||||||
|
|
||||||
baseQuantity = getRunParams().getBigDecimal("base_quantity");
|
baseQuantity = getRunParams().getBigDecimal("base_quantity");
|
||||||
atrMultiplier = getRunParams().getBigDecimal("atr_multiplier");
|
atrMultiplier = getRunParams().getBigDecimal("atr_multiplier");
|
||||||
|
gridGrpScript = getRunParams().getString("grid_grp_script");
|
||||||
klineInterval = getRunParams().getString("kline_interval");
|
klineInterval = getRunParams().getString("kline_interval");
|
||||||
klinePeriod = getRunParams().getInteger("kline_period");
|
klinePeriod = getRunParams().getInteger("kline_period");
|
||||||
gridAdjustIntervalMinutes = getRunParams().getInteger("grid_adjust_interval");
|
gridAdjustIntervalMinutes = getRunParams().getInteger("grid_adjust_interval");
|
||||||
@ -388,7 +391,17 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal rawGap = atr.multiply(atrMultiplier);
|
Map<String, Object> scriptVariables = buildGridScriptVariables(atr);
|
||||||
|
BigDecimal rawGap;
|
||||||
|
try {
|
||||||
|
rawGap = ScriptUtil.evalBigDecimal(gridGrpScript, scriptVariables);
|
||||||
|
} catch (Exception scriptException) {
|
||||||
|
logger.warn("网格表达式执行失败,回退默认公式:{}", scriptException.getMessage());
|
||||||
|
rawGap = null;
|
||||||
|
}
|
||||||
|
if (rawGap == null) {
|
||||||
|
rawGap = atr.multiply(atrMultiplier);
|
||||||
|
}
|
||||||
BigDecimal gap = rawGap.max(minGap).min(maxGap).setScale(pricePrecision, RoundingMode.HALF_UP);
|
BigDecimal gap = rawGap.max(minGap).min(maxGap).setScale(pricePrecision, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
BigDecimal oldGap = currentGridGap;
|
BigDecimal oldGap = currentGridGap;
|
||||||
@ -408,6 +421,26 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> buildGridScriptVariables(BigDecimal atr) {
|
||||||
|
Map<String, Object> variables = new java.util.HashMap<>();
|
||||||
|
variables.put("atr", atr == null ? null : atr.doubleValue());
|
||||||
|
variables.put("atrMultiplier", atrMultiplier == null ? null : atrMultiplier.doubleValue());
|
||||||
|
variables.put("baseQuantity", baseQuantity == null ? null : baseQuantity.doubleValue());
|
||||||
|
variables.put("minGap", minGap == null ? null : minGap.doubleValue());
|
||||||
|
variables.put("maxGap", maxGap == null ? null : maxGap.doubleValue());
|
||||||
|
variables.put("upperBound", upperBound == null ? null : upperBound.doubleValue());
|
||||||
|
variables.put("lowerBound", lowerBound == null ? null : lowerBound.doubleValue());
|
||||||
|
variables.put("gridCount", gridCount);
|
||||||
|
variables.put("leverage", leverage);
|
||||||
|
variables.put("pricePrecision", pricePrecision);
|
||||||
|
variables.put("quantityPrecision", quantityPrecision);
|
||||||
|
variables.put("klinePeriod", klinePeriod);
|
||||||
|
variables.put("gridAdjustIntervalMinutes", gridAdjustIntervalMinutes);
|
||||||
|
variables.put("testMode", testMode);
|
||||||
|
variables.put("currentPrice", currentPrice == null ? null : currentPrice.doubleValue());
|
||||||
|
return variables;
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal fetchAtr() throws Exception {
|
private BigDecimal fetchAtr() throws Exception {
|
||||||
JSONObject params = new JSONObject();
|
JSONObject params = new JSONObject();
|
||||||
params.put("symbol", getPlainSymbol());
|
params.put("symbol", getPlainSymbol());
|
||||||
@ -478,10 +511,8 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal nextBuyPrice = calcBuyPriceBelowCurrent();
|
BigDecimal nextBuyPrice = findNextAvailableBuyPrice(calcBuyPriceBelowCurrent());
|
||||||
if (nextBuyPrice != null
|
if (nextBuyPrice != null) {
|
||||||
&& nextBuyPrice.compareTo(lowerBound) >= 0
|
|
||||||
&& !hasBuyOrderAt(nextBuyPrice)) {
|
|
||||||
placeLimitBuyOrder(nextBuyPrice);
|
placeLimitBuyOrder(nextBuyPrice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -509,10 +540,8 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal newPrice = calcBuyPriceBelowCurrent();
|
BigDecimal newPrice = findNextAvailableBuyPrice(calcBuyPriceBelowCurrent());
|
||||||
if (newPrice == null
|
if (newPrice == null || newPrice.compareTo(pending.buyPrice) == 0) {
|
||||||
|| newPrice.compareTo(lowerBound) < 0
|
|
||||||
|| newPrice.compareTo(pending.buyPrice) == 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,8 +593,9 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BigDecimal nextBuyPrice = normalizePrice(filledPrice.subtract(currentGridGap));
|
BigDecimal nextBuyPrice = findNextAvailableBuyPrice(
|
||||||
if (nextBuyPrice.compareTo(lowerBound) >= 0 && !hasBuyOrderAt(nextBuyPrice)) {
|
normalizePrice(filledPrice.subtract(currentGridGap)));
|
||||||
|
if (nextBuyPrice != null) {
|
||||||
placeLimitBuyOrder(nextBuyPrice);
|
placeLimitBuyOrder(nextBuyPrice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -579,7 +609,7 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
if (normalizedPrice.compareTo(lowerBound) < 0 || normalizedPrice.compareTo(upperBound) > 0) {
|
if (normalizedPrice.compareTo(lowerBound) < 0 || normalizedPrice.compareTo(upperBound) > 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (hasBuyOrderAt(normalizedPrice)) {
|
if (hasBuyOrderAt(normalizedPrice) || hasOpenPositionAtPrice(normalizedPrice)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,6 +799,17 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
|
|
||||||
synchronized (tradeLock) {
|
synchronized (tradeLock) {
|
||||||
BigDecimal gridPrice = pending.buyPrice;
|
BigDecimal gridPrice = pending.buyPrice;
|
||||||
|
if (hasOpenPositionAtPrice(gridPrice)) {
|
||||||
|
if (orderId != null) {
|
||||||
|
processedBuyFillOrderIds.remove(orderId);
|
||||||
|
}
|
||||||
|
logger.warn("该价位已有持仓,忽略重复买单成交,gridPrice={},orderId={}", gridPrice, orderId);
|
||||||
|
submitAlarm("重复开仓拦截",
|
||||||
|
String.format("价位 %s 已有持仓,忽略 orderId=%s", formatPrice(gridPrice), orderId),
|
||||||
|
AlarmLevel.WARNING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String clientPositionId = "POS_" + gridPrice.toPlainString();
|
String clientPositionId = "POS_" + gridPrice.toPlainString();
|
||||||
if (currentGridGap == null) {
|
if (currentGridGap == null) {
|
||||||
if (orderId != null) {
|
if (orderId != null) {
|
||||||
@ -1263,6 +1304,36 @@ public class AutoGridStrategy extends BaseStrategy {
|
|||||||
return pendingBuyOrders.containsKey(priceKey(normalizePrice(price)));
|
return pendingBuyOrders.containsKey(priceKey(normalizePrice(price)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean hasOpenPositionAtPrice(BigDecimal price) {
|
||||||
|
if (price == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String key = priceKey(price);
|
||||||
|
for (GridPosition position : openPositions) {
|
||||||
|
if (priceKey(position.entryPrice).equals(key)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从 startPrice 起向下逐格查找:无持仓且无待成交买单的价位。
|
||||||
|
*/
|
||||||
|
private BigDecimal findNextAvailableBuyPrice(BigDecimal startPrice) {
|
||||||
|
if (startPrice == null || currentGridGap == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
BigDecimal candidate = normalizePrice(startPrice);
|
||||||
|
while (candidate.compareTo(lowerBound) >= 0) {
|
||||||
|
if (!hasOpenPositionAtPrice(candidate) && !hasBuyOrderAt(candidate)) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
candidate = normalizePrice(candidate.subtract(currentGridGap));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private BigDecimal alignPriceDown(BigDecimal price, BigDecimal gap) {
|
private BigDecimal alignPriceDown(BigDecimal price, BigDecimal gap) {
|
||||||
if (gap == null || gap.compareTo(BigDecimal.ZERO) <= 0) {
|
if (gap == null || gap.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
return normalizePrice(price);
|
return normalizePrice(price);
|
||||||
|
|||||||
45
src/main/java/com/sample/trend/strategy/util/ScriptUtil.java
Normal file
45
src/main/java/com/sample/trend/strategy/util/ScriptUtil.java
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package com.sample.trend.strategy.util;
|
||||||
|
|
||||||
|
import org.graalvm.polyglot.Context;
|
||||||
|
import org.graalvm.polyglot.HostAccess;
|
||||||
|
import org.graalvm.polyglot.Value;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class ScriptUtil {
|
||||||
|
|
||||||
|
private ScriptUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BigDecimal evalBigDecimal(String expression, Map<String, Object> variables) {
|
||||||
|
if (expression == null || expression.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try (Context context = Context.newBuilder("js")
|
||||||
|
.allowHostAccess(HostAccess.NONE)
|
||||||
|
.allowHostClassLookup(className -> false)
|
||||||
|
.build()) {
|
||||||
|
if (variables != null) {
|
||||||
|
for (Map.Entry<String, Object> entry : variables.entrySet()) {
|
||||||
|
context.getBindings("js").putMember(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Value result = context.eval("js", expression);
|
||||||
|
if (result == null || result.isNull()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (result.fitsInLong()) {
|
||||||
|
return BigDecimal.valueOf(result.asLong());
|
||||||
|
}
|
||||||
|
if (result.fitsInDouble()) {
|
||||||
|
return BigDecimal.valueOf(result.asDouble());
|
||||||
|
}
|
||||||
|
return new BigDecimal(result.toString());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalArgumentException("网格脚本执行失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user