diff --git a/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java b/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java index 7e003827..396f7b90 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java +++ b/src/main/java/com/cleanengine/coin/realitybot/api/ApiScheduler.java @@ -1,6 +1,5 @@ package com.cleanengine.coin.realitybot.api; -import com.cleanengine.coin.common.annotation.WorkingServerProfile; import com.cleanengine.coin.order.adapter.out.persistentce.asset.AssetRepository; import com.cleanengine.coin.order.domain.Asset; import com.cleanengine.coin.realitybot.domain.APIVWAPState; @@ -8,6 +7,7 @@ import com.cleanengine.coin.realitybot.dto.Ticks; import com.cleanengine.coin.realitybot.parser.TickParser; import com.cleanengine.coin.realitybot.service.OrderGenerateService; +import com.cleanengine.coin.realitybot.service.PlatformVWAPService; import com.cleanengine.coin.realitybot.service.TickServiceManager; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Timer; @@ -33,8 +33,9 @@ public class ApiScheduler { private final CoinoneAPIClient coinoneAPIClient; private final VWAPMetricsRecorder recorder; private final MeterRegistry meterRegistry; + private final PlatformVWAPService platformVWAPService; -// @Scheduled(fixedRate = 5000) + // @Scheduled(fixedRate = 5000) public void MarketAllRequest() throws InterruptedException { Timer timer = meterRegistry.timer("apischeduler.request.duration"); timer.record(() -> { @@ -53,6 +54,7 @@ public void MarketDataRequest(String ticker){ APIVWAPState apiVWAPState = tickServiceManager.getService(ticker); long lastSeqId = lastSequentialIdMap.getOrDefault(ticker,0L); + boolean newTickAdded = false; //api 중복검사하여 queue에 저장하기 for (int i = gson.size()-1; i >=0 ; i--) {//2차 : 10 - 역순으로 정렬되어 - 순회해야 함. @@ -60,17 +62,29 @@ public void MarketDataRequest(String ticker){ if (ticks.getSequential_id() > lastSeqId){ //중복 검증용 apiVWAPState.addTick(ticks); lastSeqId = Math.max(lastSeqId, ticks.getSequential_id()); //중복 id 갱신 - + newTickAdded = true; } } lastSequentialIdMap.put(ticker,lastSeqId); + double vwap = apiVWAPState.getVWAP(); double volume = apiVWAPState.getAvgVolumePerOrder(); + double platformVwap = platformVWAPService.calculateVWAPbyTrades(ticker,vwap); + double difference = Math.abs((platformVwap - vwap)/vwap); recorder.recordApiVwap(ticker,vwap); - orderGenerateService.generateOrder(ticker,vwap,volume); //1tick 당 매수/매도 3개씩 제작 + if (difference>0.001){ + orderGenerateService.generateOrder(ticker,vwap,volume); //1tick 당 매수/매도 3개씩 제작 +// log.info("기준치 초과시 {}의 가격 : {} , 볼륨 : {}, 오차 : {}",ticker, vwap, volume, difference); + } else { + if (newTickAdded){ + orderGenerateService.generateOrder(ticker,vwap,volume); +// log.info("기준치 이내 {}의 가격 : {} , 볼륨 : {}, 오차 : {}",ticker, vwap, volume, difference); + } + } + + -// log.info("작동확인 {}의 가격 : {} , 볼륨 : {}",ticker, vwap, volume); } /* @Override diff --git a/src/main/java/com/cleanengine/coin/realitybot/domain/APIVWAPState.java b/src/main/java/com/cleanengine/coin/realitybot/domain/APIVWAPState.java index aa801573..cfb7f6f6 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/domain/APIVWAPState.java +++ b/src/main/java/com/cleanengine/coin/realitybot/domain/APIVWAPState.java @@ -31,7 +31,7 @@ public void addTick(Ticks tick){ //n초마다 5회 주문 , api 체결 내역에서 10종목씩 비교 public double getAvgVolumePerOrder() { - return calculator.getTotalVolume() / 50; + return calculator.getTotalVolume() / ticksQueue.size(); }//todo 에러 인젝션으로 50일때와 5일때 복귀 속도 알아보기 public double getVWAP(){ diff --git a/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java b/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java index 4529fe97..e3e33b99 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java +++ b/src/main/java/com/cleanengine/coin/realitybot/service/OrderGenerateService.java @@ -46,19 +46,20 @@ public void generateOrder(String ticker, double apiVWAP, double avgVolume) {// recorder.recordPlatformVwap(ticker,platformVWAP); //편차 계산 (vwap 기준) double trendLineRate = (platformVWAP - apiVWAP)/ apiVWAP; + + for(int level : orderLevels) { //1주문당 3회 매수매도 처리 - OrderPricePolicy.OrderPrice basePrice = orderPricePolicy.calculatePrice(level,platformVWAP, unitPrice,trendLineRate); - DeviationPricePolicy.AdjustPrice adjustPrice = deviationPricePolicy.adjust( - basePrice.sell(), basePrice.buy(), trendLineRate, apiVWAP, unitPrice); + OrderPricePolicy.OrderPrice basePrice = orderPricePolicy.calculatePrice(level,apiVWAP,platformVWAP, unitPrice); +// DeviationPricePolicy.AdjustPrice adjustPrice = deviationPricePolicy.adjust(basePrice.sell(), basePrice.buy(), trendLineRate, apiVWAP, unitPrice); double sellVolume = orderVolumePolicy.calculateVolume(avgVolume,trendLineRate,false); double buyVolume = orderVolumePolicy.calculateVolume(avgVolume,trendLineRate,true); - double sellPrice = adjustPrice.sell(); - double buyPrice = adjustPrice.buy(); +// double sellPrice = adjustPrice.sell(); +// double buyPrice = adjustPrice.buy(); - createOrderWithFallback(ticker,false, sellVolume, sellPrice); - createOrderWithFallback(ticker,true, buyVolume, buyPrice); + createOrderWithFallback(ticker,false, sellVolume, basePrice.sell()); + createOrderWithFallback(ticker,true, buyVolume, basePrice.buy()); /* DecimalFormat df = new DecimalFormat("#,##0.00"); DecimalFormat dfv = new DecimalFormat("#,###.########"); diff --git a/src/main/java/com/cleanengine/coin/realitybot/vo/DeviationPricePolicy.java b/src/main/java/com/cleanengine/coin/realitybot/vo/DeviationPricePolicy.java index 6ef0e471..4e20cb51 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/vo/DeviationPricePolicy.java +++ b/src/main/java/com/cleanengine/coin/realitybot/vo/DeviationPricePolicy.java @@ -18,17 +18,16 @@ public class DeviationPricePolicy { public AdjustPrice adjust(double platformSell,double platformBuy, double trendLineRate, double apiVWAP, double unitPrice){ double deviation = Math.abs(trendLineRate);//음수값 보정 - if (deviation <= 0.017){ - return new AdjustPrice(platformSell,platformBuy); + // 1% 이내 편차는 조정 없이 원본 가격 반환 + if (deviation <= 0.001) { // 0.017 → 0.01로 변경 + return new AdjustPrice(platformSell, platformBuy); } + + // 1% 초과 시에만 조정 로직 실행 double weight = getCorrectionWeight(deviation); -// double closeness = 1-weight; // 보간 가중치: 0.7 ~ 1.0 -> 0.5 - double closeness; // 보간 가중치: 0.7 ~ 1.0 -> 0.5 - if (deviation > 0.07){ - closeness = Math.max(0.2, 1 - weight); - } else { - closeness = 0.01; - } + double closeness = (deviation > 0.07) + ? Math.max(0.2, 1 - weight) + : 0.01; // double targetVWAP = (trendLineRate > 0) //만약 closeness 를 0.5 입력시 중간값 // ? apiVWAP + (platformSell - apiVWAP) * closeness // 고평가 → platformSell(25000) → apiVWAP(16000) 사이 가중치 %로 유도 diff --git a/src/main/java/com/cleanengine/coin/realitybot/vo/OrderPricePolicy.java b/src/main/java/com/cleanengine/coin/realitybot/vo/OrderPricePolicy.java index fefa8582..aa99b89c 100644 --- a/src/main/java/com/cleanengine/coin/realitybot/vo/OrderPricePolicy.java +++ b/src/main/java/com/cleanengine/coin/realitybot/vo/OrderPricePolicy.java @@ -7,55 +7,38 @@ public class OrderPricePolicy { /** * 레벨에 따라 매수/매도 가격을 계산합니다. * @param level 주문 강도 (1~5) - * @param platformVWAP 플랫폼 기준 평균 체결 가격 +// * @param platformVWAP 플랫폼 기준 평균 체결 가격 * @param unitPrice 호가 단위 - * @param trendLineRate 플랫폼과 API VWAP의 편차율 +// * @param trendLineRate 플랫폼과 API VWAP의 편차율 * @return PricePair (매도/매수 가격) */ - public OrderPrice calculatePrice(int level, - double platformVWAP, - double unitPrice, - double trendLineRate) { - double priceOffset = unitPrice * level; - double sellPrice, buyPrice; - double randomOffset = Math.abs(getRandomOffset(platformVWAP,getDynamicMaxRate(trendLineRate))); - double basePrice = normalizeToUnit(platformVWAP, unitPrice); //기준 가격 (호가 단위 정규화) + public OrderPrice calculatePrice(int level, double apiVWAP, double platformVWAP, double unitPrice) { + double basePrice = normalizeToUnit(apiVWAP, unitPrice); + double targetPrice = normalizeToUnit(platformVWAP, unitPrice); + // 🔥 점진적 접근: API VWAP에서 Platform VWAP로 20% 씩 이동 + double convergenceRate = 0.2; // 천천히 접근 + double adjustedBase = basePrice + (targetPrice - basePrice) * convergenceRate; - if (level == 1){ //1level일 경우 주문이 겹치도록 설정 - //체결을 위해 매수가 올리고, 매도가 내리는 계산 적용 - sellPrice = normalizeToUnit(basePrice - randomOffset,unitPrice); - buyPrice = normalizeToUnit(basePrice + randomOffset,unitPrice); - } - //2~3 단계 : orderbook 단위 주문 - else { - randomOffset = getRandomOffset(platformVWAP,0.001); - //체결 확률 증가용 코드 - sellPrice = normalizeToUnit(platformVWAP + priceOffset - randomOffset,unitPrice); - buyPrice = normalizeToUnit(platformVWAP - priceOffset + randomOffset,unitPrice); - //안정적인 스프레드 유지 -// sellPrice = normalizeToUnit(platformVWAP + priceOffset); -// buyPrice = normalizeToUnit(platformVWAP - priceOffset); + double priceOffset = unitPrice * level; + + if (level == 1) { + // 1레벨: 조정된 기준가 근처 체결 유도 + return new OrderPrice(adjustedBase + priceOffset/2, adjustedBase - priceOffset/2); + } else { + // 2~5레벨: 조정된 기준가 기준 스프레드 + return new OrderPrice(adjustedBase + priceOffset, adjustedBase - priceOffset); } - return new OrderPrice(sellPrice, buyPrice); } - private double getRandomOffset(double basePrice, double maxRate){ - //시장가에 해당하는 호가는 거래 체결 강하게 하기 위함 - double percent = (Math.random() * 2-1)*maxRate; + private double getRandomOffset(double basePrice, double maxRate) { + double percent = (Math.random() * 2 - 1) * maxRate; return basePrice * percent; } - private double getDynamicMaxRate(double trendLineRate) { - // 편차가 벌어지면 벌어질수록 보정폭 확대 - // 5% = 2.51의 가중치 - // 11% = 5.51의 가중치 - return 0.01 + Math.abs(trendLineRate) * 0.5; - } - - private int normalizeToUnit(double price, double unitPrice){ //호가단위로 변환 - return (int) ((double)(Math.round(price / unitPrice)) * unitPrice); + private double normalizeToUnit(double price, double unitPrice) { + return Math.round(price / unitPrice) * unitPrice; } - public record OrderPrice(double sell, double buy){} -} + public record OrderPrice(double sell, double buy) {} +} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 88ad9a86..a629bbcc 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -52,6 +52,6 @@ server: forward-headers-strategy: native bot-handler: - fixed-rate: 5000 # 5초마다 실행 + fixed-rate: 1000 # 5초마다 실행 cron : "0 0 0 * * *" # 매일 자정마다 호가 - order-level : 1,2,3,4,5 #오더북 단계 설정 - 주문량 증가 + order-level : 1,2 #오더북 단계 설정 - 주문량 증가