ScriptUtil脚本实现
This commit is contained in:
parent
3bac0e4aee
commit
8ac9a8ec1a
15
pom.xml
15
pom.xml
@ -5,12 +5,13 @@
|
||||
|
||||
<groupId>com.sample</groupId>
|
||||
<artifactId>trend-grid-strategy</artifactId>
|
||||
<version>1.0.2-SNAPSHOT</version>
|
||||
<version>1.0.3-SNAPSHOT</version>
|
||||
<name>trend-grid-strategy</name>
|
||||
<description>趋势网格策略</description>
|
||||
|
||||
<properties>
|
||||
<trade.version>5.0.0-SNAPSHOT</trade.version>
|
||||
<graalvm.version>24.1.2</graalvm.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -24,6 +25,18 @@
|
||||
<artifactId>uuquant-exchange-api</artifactId>
|
||||
<version>20260619.0130</version>
|
||||
</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>
|
||||
|
||||
<build>
|
||||
|
||||
@ -2,21 +2,31 @@ package com.sample.trend;
|
||||
|
||||
|
||||
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.vo.DebugConfig;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class StrategyApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 调试模式
|
||||
DebugConfig debugConfig = new DebugConfig();
|
||||
debugConfig.setAgentHost("192.168.0.105");
|
||||
debugConfig.setRobotId(2074501096701927426L);
|
||||
debugConfig.setRobotApiKey("be7520970034b54cb7f2bccab0d546ca");
|
||||
// DebugConfig debugConfig = new DebugConfig();
|
||||
// debugConfig.setAgentHost("192.168.0.105");
|
||||
// debugConfig.setRobotId(2074501096701927426L);
|
||||
// debugConfig.setRobotApiKey("be7520970034b54cb7f2bccab0d546ca");
|
||||
//
|
||||
// StrategyRunner strategyRunner = StrategyRunner.getInstance();
|
||||
// strategyRunner.setDebugConfig(debugConfig);
|
||||
// strategyRunner.init(AutoGridStrategy.class);
|
||||
|
||||
StrategyRunner strategyRunner = StrategyRunner.getInstance();
|
||||
strategyRunner.setDebugConfig(debugConfig);
|
||||
strategyRunner.init(AutoGridStrategy.class);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("atr", 1);
|
||||
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.sample.trend.config.CommonConstant;
|
||||
import com.sample.trend.strategy.util.AtrCalculator;
|
||||
import com.sample.trend.strategy.util.ScriptUtil;
|
||||
import okhttp3.Response;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -52,6 +53,7 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
// 运行参数
|
||||
private BigDecimal baseQuantity;
|
||||
private BigDecimal atrMultiplier;
|
||||
private String gridGrpScript;
|
||||
private String klineInterval;
|
||||
private int klinePeriod;
|
||||
private int gridAdjustIntervalMinutes;
|
||||
@ -259,12 +261,13 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
super.init(context);
|
||||
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_count", "test_mode");
|
||||
|
||||
baseQuantity = getRunParams().getBigDecimal("base_quantity");
|
||||
atrMultiplier = getRunParams().getBigDecimal("atr_multiplier");
|
||||
gridGrpScript = getRunParams().getString("grid_grp_script");
|
||||
klineInterval = getRunParams().getString("kline_interval");
|
||||
klinePeriod = getRunParams().getInteger("kline_period");
|
||||
gridAdjustIntervalMinutes = getRunParams().getInteger("grid_adjust_interval");
|
||||
@ -388,7 +391,17 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
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 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 {
|
||||
JSONObject params = new JSONObject();
|
||||
params.put("symbol", getPlainSymbol());
|
||||
@ -478,10 +511,8 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal nextBuyPrice = calcBuyPriceBelowCurrent();
|
||||
if (nextBuyPrice != null
|
||||
&& nextBuyPrice.compareTo(lowerBound) >= 0
|
||||
&& !hasBuyOrderAt(nextBuyPrice)) {
|
||||
BigDecimal nextBuyPrice = findNextAvailableBuyPrice(calcBuyPriceBelowCurrent());
|
||||
if (nextBuyPrice != null) {
|
||||
placeLimitBuyOrder(nextBuyPrice);
|
||||
}
|
||||
}
|
||||
@ -509,10 +540,8 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal newPrice = calcBuyPriceBelowCurrent();
|
||||
if (newPrice == null
|
||||
|| newPrice.compareTo(lowerBound) < 0
|
||||
|| newPrice.compareTo(pending.buyPrice) == 0) {
|
||||
BigDecimal newPrice = findNextAvailableBuyPrice(calcBuyPriceBelowCurrent());
|
||||
if (newPrice == null || newPrice.compareTo(pending.buyPrice) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -564,8 +593,9 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
return;
|
||||
}
|
||||
|
||||
BigDecimal nextBuyPrice = normalizePrice(filledPrice.subtract(currentGridGap));
|
||||
if (nextBuyPrice.compareTo(lowerBound) >= 0 && !hasBuyOrderAt(nextBuyPrice)) {
|
||||
BigDecimal nextBuyPrice = findNextAvailableBuyPrice(
|
||||
normalizePrice(filledPrice.subtract(currentGridGap)));
|
||||
if (nextBuyPrice != null) {
|
||||
placeLimitBuyOrder(nextBuyPrice);
|
||||
}
|
||||
}
|
||||
@ -579,7 +609,7 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
if (normalizedPrice.compareTo(lowerBound) < 0 || normalizedPrice.compareTo(upperBound) > 0) {
|
||||
return;
|
||||
}
|
||||
if (hasBuyOrderAt(normalizedPrice)) {
|
||||
if (hasBuyOrderAt(normalizedPrice) || hasOpenPositionAtPrice(normalizedPrice)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -769,6 +799,17 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
|
||||
synchronized (tradeLock) {
|
||||
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();
|
||||
if (currentGridGap == null) {
|
||||
if (orderId != null) {
|
||||
@ -1263,6 +1304,36 @@ public class AutoGridStrategy extends BaseStrategy {
|
||||
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) {
|
||||
if (gap == null || gap.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
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