...
This commit is contained in:
@@ -247,8 +247,8 @@ class TradeConfig {
|
|||||||
var ONE_STOCK_ALYSIS_TIME = 180000L
|
var ONE_STOCK_ALYSIS_TIME = 180000L
|
||||||
var isLowPerformanceMonitoring: Boolean = false
|
var isLowPerformanceMonitoring: Boolean = false
|
||||||
var useGradeShare : List<String> = listOf("LEVEL_4","LEVEL_5")
|
var useGradeShare : List<String> = listOf("LEVEL_4","LEVEL_5")
|
||||||
var useTagsShare : List<String> = listOf("NOTICE", "WATCH","매도 완료")
|
var useTagsShare : List<String> = listOf("NOTICE", "WATCH")
|
||||||
var useLogKeywordsShare : List<String> = listOf("재분석")
|
var useLogKeywordsShare : List<String> = listOf("재분석","매도 완료")
|
||||||
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
|
||||||
@@ -268,6 +268,8 @@ class TradeConfig {
|
|||||||
var maxExpectedReboundDays: Double = 10.0 // 필터링 기준 최대 허용 반등 주기 (일)
|
var maxExpectedReboundDays: Double = 10.0 // 필터링 기준 최대 허용 반등 주기 (일)
|
||||||
var minExpectedReboundDays: Double = 1.5
|
var minExpectedReboundDays: Double = 1.5
|
||||||
var isUpcomingDividend : Boolean = false
|
var isUpcomingDividend : Boolean = false
|
||||||
|
var buyTickOffset : Int = 4
|
||||||
|
var sellTickOffset : Int = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ object AutoTradingManager {
|
|||||||
|
|
||||||
val price1 = finalPrice
|
val price1 = finalPrice
|
||||||
// 2차 매수는 1차 매수가보다 3호가 아래에 대기 (더 싼 가격에 줍기)
|
// 2차 매수는 1차 매수가보다 3호가 아래에 대기 (더 싼 가격에 줍기)
|
||||||
val price2 = MarketUtil.roundToTickSize(price1 - (tickSize * 3))
|
val price2 = MarketUtil.roundToTickSize(price1 - (tickSize * KisSession.tradeConfig.buyTickOffset))
|
||||||
|
|
||||||
println("🔄 [분할 매수 진입] $stockName 1차(${qty1}주 / $price1) | 2차(${qty2}주 / $price2)")
|
println("🔄 [분할 매수 진입] $stockName 1차(${qty1}주 / $price1) | 2차(${qty2}주 / $price2)")
|
||||||
|
|
||||||
@@ -574,7 +574,7 @@ object AutoTradingManager {
|
|||||||
val qty2 = totalQty - qty1 // 나머지 (4호가 위)
|
val qty2 = totalQty - qty1 // 나머지 (4호가 위)
|
||||||
|
|
||||||
val price1 = currentPrice
|
val price1 = currentPrice
|
||||||
val price2 = MarketUtil.roundToTickSize(currentPrice + (MarketUtil.getTickSize(currentPrice) * 4))
|
val price2 = MarketUtil.roundToTickSize(currentPrice + (MarketUtil.getTickSize(currentPrice) * KisSession.tradeConfig.sellTickOffset))
|
||||||
|
|
||||||
TradingLogStore.addAfterMarketLog(
|
TradingLogStore.addAfterMarketLog(
|
||||||
holding.name,
|
holding.name,
|
||||||
@@ -639,7 +639,7 @@ object AutoTradingManager {
|
|||||||
"${holding.name}[${holding.code}]",
|
"${holding.name}[${holding.code}]",
|
||||||
price2.toString(),
|
price2.toString(),
|
||||||
"SELL",
|
"SELL",
|
||||||
"🎊 ${if (marketCode == "Y") "시간외 단일가" else "대체거래소"} 2차 분할매도(4호가 위) ${qty2}주 매도 완료"
|
"🎊 ${if (marketCode == "Y") "시간외 단일가" else "대체거래소"} 2차 분할매도(${KisSession.tradeConfig.sellTickOffset}호가 위) ${qty2}주 매도 완료"
|
||||||
)
|
)
|
||||||
DatabaseFactory.saveAutoTrade(
|
DatabaseFactory.saveAutoTrade(
|
||||||
AutoTradeItem(
|
AutoTradeItem(
|
||||||
@@ -766,9 +766,10 @@ object AutoTradingManager {
|
|||||||
val qty2 = totalQty - qty1
|
val qty2 = totalQty - qty1
|
||||||
|
|
||||||
val price1 = currentPrice
|
val price1 = currentPrice
|
||||||
val price2 = MarketUtil.roundToTickSize(currentPrice + (MarketUtil.getTickSize(currentPrice) * 4))
|
val price2 = MarketUtil.roundToTickSize(currentPrice + (MarketUtil.getTickSize(currentPrice) * KisSession.tradeConfig.sellTickOffset))
|
||||||
|
|
||||||
println("🔄 [보유 주식 주문] ${holding.name} (${holding.code}) 분할 매도: 현재가($price1) ${qty1}주, 4호가 위($price2) ${qty2}주")
|
|
||||||
|
println("🔄 [보유 주식 주문] ${holding.name} (${holding.code}) 분할 매도: 현재가($price1) ${qty1}주, ${KisSession.tradeConfig.sellTickOffset}호가 위($price2) ${qty2}주")
|
||||||
|
|
||||||
// 1차 매도: 현재가
|
// 1차 매도: 현재가
|
||||||
if (qty1 > 0) {
|
if (qty1 > 0) {
|
||||||
@@ -1499,9 +1500,7 @@ object AutoTradingManager {
|
|||||||
if (corpInfo?.cName.isNullOrEmpty()) return@withTimeout
|
if (corpInfo?.cName.isNullOrEmpty()) return@withTimeout
|
||||||
|
|
||||||
// 🌟 [핵심] 물타기 대상 여부 플래그 식별
|
// 🌟 [핵심] 물타기 대상 여부 플래그 식별
|
||||||
val targetHolding = currentBalance?.getHoldings()?.firstOrNull {
|
val targetHolding = currentBalance?.getHoldings()?.firstOrNull { it.code == stock.code && it.quantity.toInt() > 0 }
|
||||||
it.code == stock.code && it.quantity.toInt() > KisSession.tradeConfig.lowerAverageTargetCount
|
|
||||||
}
|
|
||||||
val isWatering = targetHolding != null && KisSession.tradeConfig.lowerAveragePrice
|
val isWatering = targetHolding != null && KisSession.tradeConfig.lowerAveragePrice
|
||||||
|
|
||||||
if (isWatering) {
|
if (isWatering) {
|
||||||
@@ -1570,7 +1569,7 @@ object AutoTradingManager {
|
|||||||
// 🌟 [완화 3] 기대수익률 및 진입 타이밍 이원화
|
// 🌟 [완화 3] 기대수익률 및 진입 타이밍 이원화
|
||||||
val isProfitable: Boolean
|
val isProfitable: Boolean
|
||||||
val isValidEntryTiming: Boolean
|
val isValidEntryTiming: Boolean
|
||||||
|
val isSteadyUptrend = tempAnalyzer.checkSteadyUptrend(dailyData)
|
||||||
if (isWatering) {
|
if (isWatering) {
|
||||||
// 물타기: 평단 낮추기 목적이므로 기대수익률 1.0% 이상이면 충분
|
// 물타기: 평단 낮추기 목적이므로 기대수익률 1.0% 이상이면 충분
|
||||||
isProfitable = expectedProfitRate >= 1.0 || dailyStats.avgReboundAmplitude >= 1.5
|
isProfitable = expectedProfitRate >= 1.0 || dailyStats.avgReboundAmplitude >= 1.5
|
||||||
@@ -1586,7 +1585,7 @@ object AutoTradingManager {
|
|||||||
} else {
|
} else {
|
||||||
// 신규 진입: 기존 엄격 기준 유지
|
// 신규 진입: 기존 엄격 기준 유지
|
||||||
isProfitable = expectedProfitRate >= KisSession.tradeConfig.minExpectedProfitRate ||
|
isProfitable = expectedProfitRate >= KisSession.tradeConfig.minExpectedProfitRate ||
|
||||||
dailyStats.avgReboundAmplitude >= KisSession.tradeConfig.minExpectedProfitRate
|
(dailyStats.avgReboundAmplitude.times(0.8)) >= KisSession.tradeConfig.minExpectedProfitRate
|
||||||
|
|
||||||
val isApproaching = tempAnalyzer.checkReboundApproaching(
|
val isApproaching = tempAnalyzer.checkReboundApproaching(
|
||||||
dailyData, dailyStats.avgReboundPeriod, dailyStats.avgDropRate, dailyStats.timeTolerance
|
dailyData, dailyStats.avgReboundPeriod, dailyStats.avgDropRate, dailyStats.timeTolerance
|
||||||
@@ -1594,12 +1593,12 @@ object AutoTradingManager {
|
|||||||
isValidEntryTiming = (dailyStats.isValid && isApproaching &&
|
isValidEntryTiming = (dailyStats.isValid && isApproaching &&
|
||||||
dailyStats.avgReboundPeriod <= KisSession.tradeConfig.maxExpectedReboundDays &&
|
dailyStats.avgReboundPeriod <= KisSession.tradeConfig.maxExpectedReboundDays &&
|
||||||
dailyStats.avgReboundPeriod >= KisSession.tradeConfig.minExpectedReboundDays) ||
|
dailyStats.avgReboundPeriod >= KisSession.tradeConfig.minExpectedReboundDays) ||
|
||||||
tempAnalyzer.checkSteadyUptrend(dailyData)
|
isSteadyUptrend
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentAtr = tempAnalyzer.calculateATR(dailyData)
|
val currentAtr = tempAnalyzer.calculateATR(dailyData)
|
||||||
if (!isProfitable || !isValidEntryTiming) {
|
if (!isProfitable || !isValidEntryTiming) {
|
||||||
println("-> [${stock.name}] 목표 수익 조건 미달 (물타기:$isWatering, 수익:$isProfitable, 타이밍:$isValidEntryTiming) | 기대 수익율 : ${expectedProfitRate} || ${dailyStats.avgReboundAmplitude}")
|
println("-> [${stock.name}] 목표 수익 조건 미달 (물타기:$isWatering, 수익:$isProfitable, 타이밍:$isValidEntryTiming, 우상향:$isSteadyUptrend) | 기대 수익율 : ${expectedProfitRate} || ${dailyStats.avgReboundAmplitude}")
|
||||||
return@withTimeout
|
return@withTimeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -729,6 +729,29 @@ fun TradingDecisionLog() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
|
||||||
|
SettingInputField(
|
||||||
|
modifier = Modifier.weight(1.0f, true),
|
||||||
|
label = "분할 매수시",
|
||||||
|
initialValue = (tradeConfig.buyTickOffset).toString(),
|
||||||
|
onSave = {
|
||||||
|
tradeConfig.buyTickOffset = it.toInt()
|
||||||
|
KisSession.saveTradeConfig()
|
||||||
|
},
|
||||||
|
helperText = "매수가 기준 아래 호가로 분할 매수"
|
||||||
|
)
|
||||||
|
SettingInputField(
|
||||||
|
modifier = Modifier.weight(1.0f, true),
|
||||||
|
label = "분할 매도시",
|
||||||
|
initialValue = (tradeConfig.sellTickOffset).toString(),
|
||||||
|
onSave = {
|
||||||
|
tradeConfig.sellTickOffset = it.toInt()
|
||||||
|
KisSession.saveTradeConfig()
|
||||||
|
},
|
||||||
|
helperText = "매수가 기준 위 호가로 분할 매수"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user