...
This commit is contained in:
parent
4c27d71701
commit
9af9f46748
@ -252,6 +252,8 @@ class TradeConfig {
|
|||||||
var useAutoRepost : Boolean = false
|
var useAutoRepost : Boolean = false
|
||||||
var minusFilter : Double = 15.0
|
var minusFilter : Double = 15.0
|
||||||
var plusFilter : Double = 15.0
|
var plusFilter : Double = 15.0
|
||||||
|
var excuteCountOnMin : Int = 2
|
||||||
|
var autoSellOrder : Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -331,7 +331,7 @@ object KisTradeService {
|
|||||||
stockCode: String,
|
stockCode: String,
|
||||||
qty: String,
|
qty: String,
|
||||||
price: String,
|
price: String,
|
||||||
isBuy: Boolean,
|
isBuy: Boolean = false,
|
||||||
orderDivision: String = "",
|
orderDivision: String = "",
|
||||||
marketCode : String = "KRX"
|
marketCode : String = "KRX"
|
||||||
): Result<String> {
|
): Result<String> {
|
||||||
|
|||||||
@ -217,7 +217,7 @@ object AutoTradingManager {
|
|||||||
TradingLogStore.addNotice("SYSTEM", "LIMIT", "최대 보유 종목 도달로 신규 매수 일시 중단")
|
TradingLogStore.addNotice("SYSTEM", "LIMIT", "최대 보유 종목 도달로 신규 매수 일시 중단")
|
||||||
AutoTradingManager.addToReanalysis(RankingStock(mksc_shrn_iscd = stockCode,hts_kor_isnm = stockName))
|
AutoTradingManager.addToReanalysis(RankingStock(mksc_shrn_iscd = stockCode,hts_kor_isnm = stockName))
|
||||||
TradingLogStore.addWatchLog(decision,"WATCH","매수 실패 : 최대 보유 종목 도달로 신규 매수 일시 중단 => 재분석 대기열에 추가")
|
TradingLogStore.addWatchLog(decision,"WATCH","매수 실패 : 최대 보유 종목 도달로 신규 매수 일시 중단 => 재분석 대기열에 추가")
|
||||||
} else {
|
} else if (KisSession.isAvailBuyTime(LocalTime.now())){
|
||||||
println("basePrice : $basePrice, oneTickLowerPrice : $oneTickLowerPrice, finalPrice : $finalPrice")
|
println("basePrice : $basePrice, oneTickLowerPrice : $oneTickLowerPrice, finalPrice : $finalPrice")
|
||||||
|
|
||||||
KisTradeService.postOrder(stockCode, orderQty, finalPrice.toLong().toString(), isBuy = true)
|
KisTradeService.postOrder(stockCode, orderQty, finalPrice.toLong().toString(), isBuy = true)
|
||||||
@ -271,6 +271,17 @@ object AutoTradingManager {
|
|||||||
TradingLogStore.addLog(decision,"BUY",it.message ?: "매수 실패")
|
TradingLogStore.addLog(decision,"BUY",it.message ?: "매수 실패")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
val unfilledResult = KisTradeService.fetchUnfilledOrders()
|
||||||
|
unfilledResult.onSuccess { response ->
|
||||||
|
response.filter { it.sll_buy_dvsn_cd == "02" }.forEach { order ->
|
||||||
|
TradingLogStore.addNotice(order.prdt_name,order.pdno,"[주문 취소] 매수시간 종료 후 모든 매수 취소")
|
||||||
|
KisTradeService.cancelOrder(
|
||||||
|
order.ord_no, // 원주문번호
|
||||||
|
order.pdno
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -528,8 +539,9 @@ object AutoTradingManager {
|
|||||||
&& holding.valuationProfitAmount.toDouble() >= KisSession.config.getValues(ConfigIndex.LOSS_MAX_MONEY)) {
|
&& holding.valuationProfitAmount.toDouble() >= KisSession.config.getValues(ConfigIndex.LOSS_MAX_MONEY)) {
|
||||||
println("${holding.name} ${holding.profitRate.toDouble()} ${holding.valuationProfitAmount.toDouble()} ${KisSession.config.getValues(ConfigIndex.LOSS_MAX_MONEY)} , ${KisSession.config.getValues(ConfigIndex.LOSS_MINRATE)} , ${KisSession.config.getValues(ConfigIndex.STOP_LOSS)}")
|
println("${holding.name} ${holding.profitRate.toDouble()} ${holding.valuationProfitAmount.toDouble()} ${KisSession.config.getValues(ConfigIndex.LOSS_MAX_MONEY)} , ${KisSession.config.getValues(ConfigIndex.LOSS_MINRATE)} , ${KisSession.config.getValues(ConfigIndex.STOP_LOSS)}")
|
||||||
val profit = holding.profitRate.toDouble()
|
val profit = holding.profitRate.toDouble()
|
||||||
var targetPrice = holding.currentPrice.toDouble()
|
var targetPrice = if (KisSession.tradeConfig.autoSellOrder ) holding.avgPrice.toDouble() else holding.currentPrice.toDouble()
|
||||||
targetPrice = MarketUtil.roundToTickSize(targetPrice + (MarketUtil.getTickSize(targetPrice) * 3.0))
|
targetPrice = MarketUtil.roundToTickSize(targetPrice + MarketUtil.getTickSize(targetPrice) * 3.0)
|
||||||
|
|
||||||
|
|
||||||
tradeService.postOrder(
|
tradeService.postOrder(
|
||||||
stockCode = holding.code,
|
stockCode = holding.code,
|
||||||
@ -764,15 +776,6 @@ object AutoTradingManager {
|
|||||||
suspend fun checkBalance(isMorning: Boolean = true) {
|
suspend fun checkBalance(isMorning: Boolean = true) {
|
||||||
if (isMorning) {
|
if (isMorning) {
|
||||||
currentBalance = KisTradeService.fetchIntegratedBalance().getOrNull()
|
currentBalance = KisTradeService.fetchIntegratedBalance().getOrNull()
|
||||||
// currentBalance?.let { currentBalance ->
|
|
||||||
// if (LocalTime.now().isBefore(LocalTime.of(18,1))) {
|
|
||||||
// TradingReportManager.recordAssetSnapshot(
|
|
||||||
// if (LocalTime.now().isAfter(LocalTime.of(18, 0))
|
|
||||||
// ) SnapshotType.END else SnapshotType.MIDDLE, currentBalance, ""
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (KisSession.config.take_profit) currentBalance?.let { resumePendingSellOrders(KisTradeService, it) }
|
if (KisSession.config.take_profit) currentBalance?.let { resumePendingSellOrders(KisTradeService, it) }
|
||||||
if (KisSession.tradeConfig.auto_cancel_pending_buy) { checkAndCancelPendingBuyOrders() }
|
if (KisSession.tradeConfig.auto_cancel_pending_buy) { checkAndCancelPendingBuyOrders() }
|
||||||
} else {
|
} else {
|
||||||
@ -791,14 +794,10 @@ object AutoTradingManager {
|
|||||||
|
|
||||||
val orderTimeMillis = parseOrderTime(order.ord_tmd)
|
val orderTimeMillis = parseOrderTime(order.ord_tmd)
|
||||||
val elapsedMillis = currentTime - orderTimeMillis
|
val elapsedMillis = currentTime - orderTimeMillis
|
||||||
|
if (elapsedMillis >= KisSession.tradeConfig.auto_cancel_pending_time ) {
|
||||||
// 조건 A: 설정된 대기 시간 경과 여부
|
|
||||||
if (elapsedMillis >= KisSession.tradeConfig.auto_cancel_pending_time) {
|
|
||||||
|
|
||||||
// 2. 현재가 조회 (가격을 비교하기 위해)
|
// 2. 현재가 조회 (가격을 비교하기 위해)
|
||||||
val currentPrice = KisTradeService.fetchCurrentPrice(order.pdno).getOrNull()?.stck_prpr?.toDouble() ?: 0.0
|
val currentPrice = KisTradeService.fetchCurrentPrice(order.pdno).getOrNull()?.stck_prpr?.toDouble() ?: 0.0
|
||||||
val orderedPrice = order.ord_unpr.toDoubleOrNull() ?: 0.0
|
val orderedPrice = order.ord_unpr.toDoubleOrNull() ?: 0.0
|
||||||
|
|
||||||
// 조건 B: 현재가와 주문가의 괴리율 체크 (현재가가 너무 올라갔거나 내려갔을 때)
|
// 조건 B: 현재가와 주문가의 괴리율 체크 (현재가가 너무 올라갔거나 내려갔을 때)
|
||||||
val priceGap = Math.abs(currentPrice - orderedPrice) / orderedPrice
|
val priceGap = Math.abs(currentPrice - orderedPrice) / orderedPrice
|
||||||
println("checkAndCancelPendingBuyOrders order $order ${elapsedMillis / 1000L}초 ${priceGap}% 차이")
|
println("checkAndCancelPendingBuyOrders order $order ${elapsedMillis / 1000L}초 ${priceGap}% 차이")
|
||||||
@ -916,53 +915,62 @@ object AutoTradingManager {
|
|||||||
}
|
}
|
||||||
println("⏱️ [Cycle End] ${LocalTime.now()}")
|
println("⏱️ [Cycle End] ${LocalTime.now()}")
|
||||||
}
|
}
|
||||||
private var lastForceCheckMinute = -1 // 마지막으로 강제 체크를 수행한 '분'을 저장
|
// private var lastForceCheckMinute = -1 // 마지막으로 강제 체크를 수행한 '분'을 저장
|
||||||
|
private val executionCountMap = mutableMapOf<String, Int>()
|
||||||
suspend fun sellSchedule() {
|
suspend fun sellSchedule() {
|
||||||
if (KisSession.config.take_profit == false) {
|
if (KisSession.config.take_profit == false) return
|
||||||
|
val now = LocalTime.now()
|
||||||
|
val timeKey = String.format("%02d:%02d", now.hour, now.minute) // 예: "09:05"
|
||||||
|
val currentCount = executionCountMap.getOrDefault(timeKey, 0)
|
||||||
|
if (currentCount >= KisSession.tradeConfig.excuteCountOnMin) { return }
|
||||||
|
|
||||||
} else {
|
var isExecuted = false
|
||||||
val now = LocalTime.now()
|
val currentMinute = now.minute
|
||||||
val currentMinute = now.minute
|
if (now.isBefore(LocalTime.of(8,50)) && now.isAfter(LocalTime.of(8,45))) {
|
||||||
if (now.hour == 8 && currentMinute < 50 && currentMinute > 45) {
|
cancelAllPendingSellOrders()
|
||||||
if (lastForceCheckMinute != currentMinute) {
|
isExecuted = true
|
||||||
cancelAllPendingSellOrders()
|
} else if ( (now.isBefore(LocalTime.of(16,0)) && now.isAfter(KisSession.endBuyTime())) ) {
|
||||||
}
|
val unfilledResult = KisTradeService.fetchUnfilledOrders()
|
||||||
} else if (now.hour == 8 && currentMinute > 55 && currentMinute > 50) {
|
unfilledResult.onSuccess { response ->
|
||||||
|
response.filter { it.sll_buy_dvsn_cd == "02" }.forEach { order ->
|
||||||
} else if (now.hour == 9 && currentMinute % 2 == 1
|
TradingLogStore.addNotice(order.prdt_name,order.pdno,"[주문 취소] 정규장 후 모든 매수 취소")
|
||||||
) {
|
KisTradeService.cancelOrder(
|
||||||
if (lastForceCheckMinute != currentMinute) {
|
order.ord_no, // 원주문번호
|
||||||
TradingLogStore.addAnalyzer(
|
order.pdno
|
||||||
" - ",
|
|
||||||
" - ",
|
|
||||||
"⏰ [강제 스케줄 실행] 오전 9시 ${currentMinute}분 - 보유주식 매도 체크를 시작합니다.",
|
|
||||||
true
|
|
||||||
)
|
)
|
||||||
println("⏰ [강제 스케줄 실행] 오전 9시 ${currentMinute}분 - 보유주식 매도 체크를 시작합니다.")
|
|
||||||
checkBalance()
|
|
||||||
lastForceCheckMinute = currentMinute // 실행 완료 기록
|
|
||||||
}
|
|
||||||
} else if (((now.hour == 8 && KisSession.tradeConfig.before_nxt && currentMinute < 45) || (now.hour >= 16 && now.hour < 20 && KisSession.tradeConfig.after_nxt)) && (currentMinute % 2 == 1)) {
|
|
||||||
if (lastForceCheckMinute != currentMinute) {
|
|
||||||
TradingLogStore.addAnalyzer(
|
|
||||||
" - ",
|
|
||||||
" - ",
|
|
||||||
"⏰ [강제 스케줄 실행] 오후 ${now.hour}시 ${currentMinute}분 - 보유주식 시간외 단일가 또는 대체마켓 체크를 시작합니다.",
|
|
||||||
true
|
|
||||||
)
|
|
||||||
var list = mutableListOf<String>("X")
|
|
||||||
if (now.hour != 8 && now.hour < 18) {
|
|
||||||
list.add("Y")
|
|
||||||
}
|
|
||||||
list.forEach { code ->
|
|
||||||
KisTradeService.fetchIntegratedBalance(code).getOrNull()?.let {
|
|
||||||
sellingAfterMarketOnePrice(KisTradeService, it, code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastForceCheckMinute = currentMinute // 실행 완료 기록
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isExecuted = true
|
||||||
|
} else if (now.hour == 9) {
|
||||||
|
TradingLogStore.addAnalyzer(
|
||||||
|
" - ",
|
||||||
|
" - ",
|
||||||
|
"⏰ [강제 스케줄 실행] 오전 9시 ${currentMinute}분 - 보유주식 매도 체크를 시작합니다.",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
println("⏰ [강제 스케줄 실행] 오전 9시 ${currentMinute}분 - 보유주식 매도 체크를 시작합니다.")
|
||||||
|
checkBalance()
|
||||||
|
isExecuted = true
|
||||||
|
} else if (((now.hour == 8 && KisSession.tradeConfig.before_nxt && currentMinute < 45) || (now.hour >= 16 && now.hour < 20 && KisSession.tradeConfig.after_nxt)) && (currentMinute % 2 == 1)) {
|
||||||
|
TradingLogStore.addAnalyzer(
|
||||||
|
" - ",
|
||||||
|
" - ",
|
||||||
|
"⏰ [강제 스케줄 실행] 오후 ${now.hour}시 ${currentMinute}분 - 보유주식 시간외 단일가 또는 대체마켓 체크를 시작합니다.",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
var list = mutableListOf<String>("X")
|
||||||
|
if (now.hour != 8 && now.hour < 18) {
|
||||||
|
list.add("Y")
|
||||||
|
}
|
||||||
|
list.forEach { code ->
|
||||||
|
KisTradeService.fetchIntegratedBalance(code).getOrNull()?.let {
|
||||||
|
sellingAfterMarketOnePrice(KisTradeService, it, code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isExecuted = true
|
||||||
}
|
}
|
||||||
|
if (isExecuted) { executionCountMap[timeKey] = currentCount + 1 }
|
||||||
|
if (now.hour >= 20) { executionCountMap.clear() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -11238,5 +11238,145 @@
|
|||||||
{
|
{
|
||||||
"code": "238490",
|
"code": "238490",
|
||||||
"name": "힘스"
|
"name": "힘스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000980",
|
||||||
|
"name": "교보19호스팩"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "001320",
|
||||||
|
"name": "교보20호스팩"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "478340",
|
||||||
|
"name": "나라스페이스테크놀로지"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "403850",
|
||||||
|
"name": "더핑크퐁컴퍼니"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000010",
|
||||||
|
"name": "덕양에너젠"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "491000",
|
||||||
|
"name": "리브스메드"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "394420",
|
||||||
|
"name": "리센스메디컬"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000930",
|
||||||
|
"name": "미래에셋비전스팩8호"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000960",
|
||||||
|
"name": "미래에셋비전스팩9호"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "488900",
|
||||||
|
"name": "비츠로넥스텍"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "001150",
|
||||||
|
"name": "삼성스팩13호"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000130",
|
||||||
|
"name": "삼진식품"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "061090",
|
||||||
|
"name": "세나테크놀로지"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "490470",
|
||||||
|
"name": "세미파이브"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "001300",
|
||||||
|
"name": "신한제17호스팩"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "388210",
|
||||||
|
"name": "씨엠티엑스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "493280",
|
||||||
|
"name": "아이엠바이오로직스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "476830",
|
||||||
|
"name": "알지노믹스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "459550",
|
||||||
|
"name": "알트"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000110",
|
||||||
|
"name": "액스비스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "458350",
|
||||||
|
"name": "에스팀"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000090",
|
||||||
|
"name": "에임드바이오"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "001050",
|
||||||
|
"name": "유진스팩12호"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "469610",
|
||||||
|
"name": "이노테크"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "261520",
|
||||||
|
"name": "이지스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "493330",
|
||||||
|
"name": "지에프아이"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "000820",
|
||||||
|
"name": "카나프테라퓨틱스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "439960",
|
||||||
|
"name": "코스모로보틱스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "464490",
|
||||||
|
"name": "쿼드메디슨"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "494120",
|
||||||
|
"name": "큐리오시스"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "466690",
|
||||||
|
"name": "키움히어로제1호스팩"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "001310",
|
||||||
|
"name": "키움히어로제2호스팩"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "487580",
|
||||||
|
"name": "폴레드"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "001010",
|
||||||
|
"name": "하나36호스팩"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"code": "408470",
|
||||||
|
"name": "한패스"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
Loading…
x
Reference in New Issue
Block a user