....
This commit is contained in:
parent
0b7684a176
commit
794b3fb5cb
@ -321,7 +321,8 @@ object KisTradeService {
|
|||||||
stockCode: String,
|
stockCode: String,
|
||||||
qty: String,
|
qty: String,
|
||||||
price: String,
|
price: String,
|
||||||
isBuy: Boolean
|
isBuy: Boolean,
|
||||||
|
orderDivision: String = ""
|
||||||
): Result<String> {
|
): Result<String> {
|
||||||
val config = KisSession.config
|
val config = KisSession.config
|
||||||
val isDomestic = stockCode.length == 6 && stockCode.all { it.isDigit() }
|
val isDomestic = stockCode.length == 6 && stockCode.all { it.isDigit() }
|
||||||
@ -339,6 +340,12 @@ object KisTradeService {
|
|||||||
isDomestic && !config.isSimulation -> if (isBuy) "TTTC0802U" else "TTTC0801U"
|
isDomestic && !config.isSimulation -> if (isBuy) "TTTC0802U" else "TTTC0801U"
|
||||||
else -> if (isBuy) "TTTS3002U" else "TTTS3001U"
|
else -> if (isBuy) "TTTS3002U" else "TTTS3001U"
|
||||||
}
|
}
|
||||||
|
val finalOrderDivision = when {
|
||||||
|
orderDivision.isNotEmpty() -> orderDivision // 외부에서 넘겨준 코드 우선 (02, 03, 34 등)
|
||||||
|
price == "0" || price.isEmpty() -> "01" // 시장가
|
||||||
|
else -> "00" // 지정가
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
val response = client.post("$baseUrl/uapi/${if(isDomestic) "domestic" else "overseas"}-stock/v1/trading/order-cash") {
|
val response = client.post("$baseUrl/uapi/${if(isDomestic) "domestic" else "overseas"}-stock/v1/trading/order-cash") {
|
||||||
@ -353,7 +360,7 @@ object KisTradeService {
|
|||||||
"CANO" to cano,
|
"CANO" to cano,
|
||||||
"ACNT_PRDT_CD" to acntPrdtCd,
|
"ACNT_PRDT_CD" to acntPrdtCd,
|
||||||
"PDNO" to stockCode,
|
"PDNO" to stockCode,
|
||||||
"ORD_DVSN" to if (price == "0" || price.isEmpty()) "01" else "00",
|
"ORD_DVSN" to finalOrderDivision,
|
||||||
"ORD_QTY" to qty,
|
"ORD_QTY" to qty,
|
||||||
"ORD_UNPR" to if (price.isEmpty() || price == "0") "0" else price
|
"ORD_UNPR" to if (price.isEmpty() || price == "0") "0" else price
|
||||||
))
|
))
|
||||||
|
|||||||
@ -365,10 +365,8 @@ object AutoTradingManager {
|
|||||||
runDiscoveryLoop(globalCallback)
|
runDiscoveryLoop(globalCallback)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun sellingAfterMarketOnePrice(tradeService: KisTradeService,balance : UnifiedBalance) {
|
||||||
suspend fun resumePendingSellOrders(tradeService: KisTradeService,balance : UnifiedBalance) {
|
println("sellingAfterMarketOnePrice")
|
||||||
// 1. DB에서 매도 중(SELLING)이거나 만료(EXPIRED)된 매도 건을 가져옵니다.
|
|
||||||
println("resumePendingSellOrders")
|
|
||||||
balance.holdings.forEach { holding ->
|
balance.holdings.forEach { holding ->
|
||||||
if (BLACKLISTEDSTOCKCODES.contains(holding.code)){
|
if (BLACKLISTEDSTOCKCODES.contains(holding.code)){
|
||||||
println("❌ 차단 처리된 주식 : ${holding.name}")
|
println("❌ 차단 처리된 주식 : ${holding.name}")
|
||||||
@ -378,46 +376,100 @@ object AutoTradingManager {
|
|||||||
"거랙 차단 대상 : ${holding.currentPrice}[${holding.quantity}주] 보유, 수익률(${holding.profitRate.toDouble()})"
|
"거랙 차단 대상 : ${holding.currentPrice}[${holding.quantity}주] 보유, 수익률(${holding.profitRate.toDouble()})"
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
if (holding != null && holding.quantity.toInt() > 0 && holding.availOrderCount.toInt() > 0 && holding.profitRate.toDouble() > KisSession.config.SELL_PROFIT) {
|
println("${holding.name} - 매수 : ${holding.avgPrice} - 현재 : ${holding.currentPrice} , 주문 가능 : ${holding.availOrderCount}, 수익율 : ${holding.profitRate}")
|
||||||
|
if (holding != null && holding.quantity.toInt() > 0 && holding.availOrderCount.toInt() > 0 && holding.profitRate.toDouble() > 0.5) {
|
||||||
println("${holding.name} - 매수 : ${holding.avgPrice} - 현재 : ${holding.currentPrice} ")
|
println("${holding.name} - 매수 : ${holding.avgPrice} - 현재 : ${holding.currentPrice} ")
|
||||||
|
|
||||||
// 3. 기존 목표가(targetPrice)로 다시 매도 주문 전송
|
|
||||||
var targetPrice = holding.currentPrice.toDouble()
|
var targetPrice = holding.currentPrice.toDouble()
|
||||||
targetPrice = MarketUtil.roundToTickSize(targetPrice + MarketUtil.getTickSize(targetPrice))
|
targetPrice = MarketUtil.roundToTickSize(targetPrice)
|
||||||
|
|
||||||
println("🔄 [재주문] ${holding.name} (${holding.code}) 매도 목표 ${targetPrice} 미체결 매도 건 재주문 시도")
|
|
||||||
// TradingLogStore.addSellLog(holding.code,targetPrice.toString(),"SELL","🎊 보유 주식 매도 주문[예상수익 : ${holding.profitRate}] ")
|
|
||||||
tradeService.postOrder(
|
tradeService.postOrder(
|
||||||
stockCode = holding.code,
|
stockCode = holding.code,
|
||||||
qty = holding.availOrderCount,
|
qty = holding.availOrderCount,
|
||||||
price = targetPrice.toInt().toString(),
|
price = targetPrice.toInt().toString(),
|
||||||
isBuy = false
|
isBuy = false,
|
||||||
|
"34"
|
||||||
).onSuccess { newOrderNo ->
|
).onSuccess { newOrderNo ->
|
||||||
// 4. 새로운 주문번호로 DB 업데이트 및 상태를 SELLING으로 유지
|
|
||||||
// DatabaseFactory.updateStatusAndOrderNo(item.id!!, TradeStatus.SELLING, newOrderNo)
|
|
||||||
println("✅ [재주문 완료] ${holding.name}: $newOrderNo")
|
println("✅ [재주문 완료] ${holding.name}: $newOrderNo")
|
||||||
TradingLogStore.addSellLog(
|
TradingLogStore.addSellLog(
|
||||||
holding.code,
|
holding.code,
|
||||||
targetPrice.toString(),
|
targetPrice.toString(),
|
||||||
"SELL",
|
"SELL",
|
||||||
"🎊 보유 주식 매도 주문 완료[예상수익 : ${holding.profitRate}] "
|
"🎊 시간외 단일가 주식 재고털이 주문 완료"
|
||||||
)
|
)
|
||||||
}.onFailure {
|
}.onFailure {
|
||||||
TradingLogStore.addSellLog(
|
TradingLogStore.addSellLog(
|
||||||
holding.code,
|
holding.code,
|
||||||
targetPrice.toString(),
|
targetPrice.toString(),
|
||||||
"SELL",
|
"SELL",
|
||||||
"🎊 보유 주식 매도 주문 실패[${it.message}] "
|
"🎊 시간외 단일가 주식 재고털이 주문 실패[${it.message}] "
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
TradingLogStore.addAnalyzer(
|
|
||||||
"보유주식[${holding.name}]",
|
|
||||||
holding.code,
|
|
||||||
"수익률 미달 : ${holding.currentPrice}[${holding.quantity}주] 보유, 수익률(${holding.profitRate.toDouble()})"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
delay(200) // API 호출 부하 방지
|
delay(300) // API 호출 부하 방지
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
suspend fun resumePendingSellOrders(tradeService: KisTradeService,balance : UnifiedBalance) {
|
||||||
|
if (isRunning()) return
|
||||||
|
val now = LocalTime.now()
|
||||||
|
val currentMinute = now.minute
|
||||||
|
if (now.isBefore(H16) && now.isAfter(H08M35)) {
|
||||||
|
println("resumePendingSellOrders")
|
||||||
|
balance.holdings.forEach { holding ->
|
||||||
|
if (BLACKLISTEDSTOCKCODES.contains(holding.code)){
|
||||||
|
println("❌ 차단 처리된 주식 : ${holding.name}")
|
||||||
|
TradingLogStore.addAnalyzer(
|
||||||
|
holding.name,
|
||||||
|
holding.code,
|
||||||
|
"거랙 차단 대상 : ${holding.currentPrice}[${holding.quantity}주] 보유, 수익률(${holding.profitRate.toDouble()})"
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (holding != null && holding.quantity.toInt() > 0 && holding.availOrderCount.toInt() > 0 && holding.profitRate.toDouble() > KisSession.config.SELL_PROFIT) {
|
||||||
|
println("${holding.name} - 매수 : ${holding.avgPrice} - 현재 : ${holding.currentPrice} ")
|
||||||
|
|
||||||
|
// 3. 기존 목표가(targetPrice)로 다시 매도 주문 전송
|
||||||
|
var targetPrice = holding.currentPrice.toDouble()
|
||||||
|
val now = LocalTime.now()
|
||||||
|
val currentMinute = now.minute
|
||||||
|
var isBefore930 = false
|
||||||
|
if (now.hour == 9 && currentMinute < 30) {
|
||||||
|
targetPrice = targetPrice
|
||||||
|
isBefore930 = true
|
||||||
|
} else {
|
||||||
|
targetPrice = MarketUtil.roundToTickSize(targetPrice + MarketUtil.getTickSize(targetPrice))
|
||||||
|
}
|
||||||
|
println("🔄 [재주문] ${holding.name} (${holding.code}) 매도 목표 ${targetPrice} 미체결 매도 건 재주문 시도")
|
||||||
|
tradeService.postOrder(
|
||||||
|
stockCode = holding.code,
|
||||||
|
qty = holding.availOrderCount,
|
||||||
|
price = targetPrice.toInt().toString(),
|
||||||
|
isBuy = false,
|
||||||
|
).onSuccess { newOrderNo ->
|
||||||
|
println("✅ [재주문 완료] ${holding.name}: $newOrderNo")
|
||||||
|
TradingLogStore.addSellLog(
|
||||||
|
holding.code,
|
||||||
|
targetPrice.toString(),
|
||||||
|
"SELL",
|
||||||
|
"🎊 보유 주식[예상수익 : ${holding.profitRate}] ${if (isBefore930) "09:30 이전 현시세{${holding.currentPrice}}로 매도[$targetPrice] 주문" else "09:30 이후 시세{${holding.currentPrice}} 기준 호가 위 매도[$targetPrice] 주문"} 완료"
|
||||||
|
)
|
||||||
|
}.onFailure {
|
||||||
|
TradingLogStore.addSellLog(
|
||||||
|
holding.code,
|
||||||
|
targetPrice.toString(),
|
||||||
|
"SELL",
|
||||||
|
"🎊 보유 주식 매도 주문 실패[${it.message}] "
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TradingLogStore.addAnalyzer(
|
||||||
|
"보유주식[${holding.name}]",
|
||||||
|
holding.code,
|
||||||
|
"수익률 미달 : ${holding.currentPrice}[${holding.quantity}주] 보유, 수익률(${holding.profitRate.toDouble()})"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
delay(200) // API 호출 부하 방지
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -464,6 +516,7 @@ object AutoTradingManager {
|
|||||||
var now = LocalTime.now(ZoneId.of("Asia/Seoul"))
|
var now = LocalTime.now(ZoneId.of("Asia/Seoul"))
|
||||||
var currentTimeMillis = System.currentTimeMillis()
|
var currentTimeMillis = System.currentTimeMillis()
|
||||||
var waitTime = 0.2
|
var waitTime = 0.2
|
||||||
|
val H16 = LocalTime.of(16, 0)
|
||||||
val H18 = LocalTime.of(18, 0)
|
val H18 = LocalTime.of(18, 0)
|
||||||
val H08M35 = LocalTime.of(8, 35)
|
val H08M35 = LocalTime.of(8, 35)
|
||||||
val H08M30 = LocalTime.of(8, 30)
|
val H08M30 = LocalTime.of(8, 30)
|
||||||
@ -558,9 +611,13 @@ object AutoTradingManager {
|
|||||||
return batch
|
return batch
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun checkBalance() : UnifiedBalance? {
|
suspend fun checkBalance(isMorning: Boolean = true) : UnifiedBalance? {
|
||||||
val balance = KisTradeService.fetchIntegratedBalance().getOrNull()
|
val balance = KisTradeService.fetchIntegratedBalance().getOrNull()
|
||||||
if (AUTOSELL) balance?.let { resumePendingSellOrders(KisTradeService, it) }
|
if (isMorning) {
|
||||||
|
if (AUTOSELL) balance?.let { resumePendingSellOrders(KisTradeService, it) }
|
||||||
|
} else {
|
||||||
|
balance?.let { sellingAfterMarketOnePrice(KisTradeService, it) }
|
||||||
|
}
|
||||||
return balance
|
return balance
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,18 +669,20 @@ object AutoTradingManager {
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
totalCount--
|
totalCount--
|
||||||
val stock = iterator.next()
|
val stock = iterator.next()
|
||||||
if (BLACKLISTEDSTOCKCODES.contains(stock.code)){
|
if (now.isBefore(H16) && now.isAfter(H08M35)) {
|
||||||
println("❌ 차단 처리된 주식 : ${stock.name}")
|
if (BLACKLISTEDSTOCKCODES.contains(stock.code)) {
|
||||||
} else {
|
println("❌ 차단 처리된 주식 : ${stock.name}")
|
||||||
try {
|
} else {
|
||||||
processSingleStock(stock, myCash, KisTradeService, globalCallback)
|
try {
|
||||||
} catch (e: Exception) {
|
processSingleStock(stock, myCash, KisTradeService, globalCallback)
|
||||||
println("❌ 처리 중 오류 발생 (건너뜀): ${stock.name}")
|
} catch (e: Exception) {
|
||||||
} finally {
|
println("❌ 처리 중 오류 발생 (건너뜀): ${stock.name}")
|
||||||
iterator.remove()
|
} finally {
|
||||||
|
iterator.remove()
|
||||||
|
}
|
||||||
|
println("남은 후보군 개수 : ${totalCount}")
|
||||||
|
delay(100)
|
||||||
}
|
}
|
||||||
println("남은 후보군 개수 : ${totalCount}")
|
|
||||||
delay(100)
|
|
||||||
}
|
}
|
||||||
sellSchedule()
|
sellSchedule()
|
||||||
}
|
}
|
||||||
@ -643,12 +702,19 @@ object AutoTradingManager {
|
|||||||
lastForceCheckMinute = currentMinute // 실행 완료 기록
|
lastForceCheckMinute = currentMinute // 실행 완료 기록
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// else if(now.hour % 2 == 1 && (currentMinute == 43)) {
|
else if((now.hour == 16 || now.hour == 17) && (currentMinute == 5 || currentMinute == 55)) {
|
||||||
// TradingLogStore.addAnalyzer(" - ", " - ", "⏰ [강제 스케줄 실행] 오전 9시 ${currentMinute}분 - 보유주식 매도 체크를 시작합니다.", true)
|
if (lastForceCheckMinute != currentMinute) {
|
||||||
// println("⏰ [강제 스케줄 실행] 오전 ${now.hour}시 ${currentMinute}분 - 보유주식 매도 체크를 시작합니다.")
|
TradingLogStore.addAnalyzer(
|
||||||
// checkBalance()
|
" - ",
|
||||||
// lastForceCheckMinute = currentMinute // 실행 완료 기록
|
" - ",
|
||||||
// }
|
"⏰ [강제 스케줄 실행] 오후 ${now.hour}시 ${currentMinute}분 - 보유주식 시간외 단일가 매도 체크를 시작합니다.",
|
||||||
|
true
|
||||||
|
)
|
||||||
|
println("⏰ [강제 스케줄 실행] 오후 ${now.hour}시 ${currentMinute}분 - 보유주식 시간외 단일가 매도 체크를 시작합니다.")
|
||||||
|
checkBalance(false)
|
||||||
|
lastForceCheckMinute = currentMinute // 실행 완료 기록
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun finalizeMarketClose(now: LocalTime) {
|
suspend fun finalizeMarketClose(now: LocalTime) {
|
||||||
|
|||||||
@ -63,7 +63,7 @@ fun SettingsScreen(onAuthSuccess: () -> Unit) {
|
|||||||
|
|
||||||
KisSession.config = config
|
KisSession.config = config
|
||||||
DatabaseFactory.saveConfig(config)
|
DatabaseFactory.saveConfig(config)
|
||||||
|
|
||||||
DartCodeManager.updateCorpCodes(HttpClient(CIO) {
|
DartCodeManager.updateCorpCodes(HttpClient(CIO) {
|
||||||
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
|
install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) }
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user