This commit is contained in:
lunaticbum 2026-05-29 13:29:17 +09:00
parent 74c3e2462d
commit 3fcce5e5c6
3 changed files with 114 additions and 9 deletions

View File

@ -256,10 +256,10 @@ class TradeConfig {
var autoSellOrder : Boolean = false var autoSellOrder : Boolean = false
var excuteMinCheck : Int = 2 var excuteMinCheck : Int = 2
var noticeGapTime : Int = 60 var noticeGapTime : Int = 60
var lowerAveragePrice : Boolean = false var lowerAveragePrice : Boolean = true
var lowerAverageStockCount : Int = 1 var lowerAverageStockCount : Int = 1
var lowerAverageMaxRate : Double = 1.0 var lowerAverageMaxRate : Double = 15.0
var lowerAverageMinRate : Double = 40.0 var lowerAverageMinRate : Double = 25.0
var lowerAverageTargetCount : Int = 2 var lowerAverageTargetCount : Int = 2
} }

View File

@ -219,7 +219,7 @@ object AutoTradingManager {
var stockName = decision.stockName var stockName = decision.stockName
val finalPrice = MarketUtil.roundToTickSize(oneTickLowerPrice.toDouble()) val finalPrice = MarketUtil.roundToTickSize(oneTickLowerPrice.toDouble())
val maxStocks = KisSession.config.getValues(ConfigIndex.MAX_HOLDING_COUNT).toInt() val maxStocks = KisSession.config.getValues(ConfigIndex.MAX_HOLDING_COUNT).toInt()
var hasCodes = currentBalance?.getHoldings()?.any { it.code.equals(decision.stockCode) && it.quantity.toInt() > 2 && !it.isTodayEntry}
if (!canAddNewPosition(maxStocks)) { if (!canAddNewPosition(maxStocks)) {
TradingLogStore.addNotice("SYSTEM", "LIMIT", "최대 보유 종목 도달로 신규 매수 일시 중단") TradingLogStore.addNotice("SYSTEM", "LIMIT", "최대 보유 종목 도달로 신규 매수 일시 중단")
@ -702,7 +702,7 @@ object AutoTradingManager {
// 현재 노출 수가 최대 허용치보다 작을 때만 true(매수 가능) 반환 // 현재 노출 수가 최대 허용치보다 작을 때만 true(매수 가능) 반환
return true//myOredsAndBalanceCodes.size < maxAllowedStocks return true //(currentBalance?.getHoldings()?.count { it.availOrderCount.toInt() > 0 } ?: 0) > maxAllowedStocks
} }
suspend fun tryRefreshToken() { suspend fun tryRefreshToken() {
@ -942,7 +942,10 @@ object AutoTradingManager {
reanalysisList.clear() reanalysisList.clear()
if (KisSession.tradeConfig.lowerAveragePrice) { if (KisSession.tradeConfig.lowerAveragePrice) {
currentBalance?.getHoldings()?.map { currentBalance?.getHoldings()?.map {
if(!it.isTodayEntry && it.quantity.toInt() > KisSession.tradeConfig.lowerAverageTargetCount && it.profitRate.toDouble() < (KisSession.tradeConfig.lowerAverageMaxRate * -1) && it.profitRate.toDouble() > (KisSession.tradeConfig.lowerAverageMinRate * -1) ) { if(!it.isTodayEntry &&
it.quantity.toInt() > KisSession.tradeConfig.lowerAverageTargetCount &&
it.profitRate.toDouble() < (KisSession.tradeConfig.lowerAverageMaxRate * -1) &&
it.profitRate.toDouble() > (KisSession.tradeConfig.lowerAverageMinRate * -1) ) {
remainingCandidates.add(RankingStock(mksc_shrn_iscd = it.code, hts_kor_isnm = it.name)) remainingCandidates.add(RankingStock(mksc_shrn_iscd = it.code, hts_kor_isnm = it.name))
} }
} }

View File

@ -571,7 +571,56 @@ fun TradingDecisionLog() {
}, },
helperText = "본인의 텔레그램 아뒤" helperText = "본인의 텔레그램 아뒤"
) )
SettingSwitchField(
label = "물타기",
initialChecked = tradeConfig.lowerAveragePrice,
onCheckedChange = { tradeConfig.lowerAveragePrice = it
KisSession.saveTradeConfig() }
)
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
SettingInputField(
modifier = Modifier.weight(1.0f, true),
label = "물타기 최저선",
initialValue = (tradeConfig.lowerAverageMaxRate).toString(),
onSave = {
tradeConfig.lowerAverageMaxRate = it.toDouble()
KisSession.saveTradeConfig()
},
helperText = "이것보다 커야 삼"
)
SettingInputField(
modifier = Modifier.weight(1.0f, true),
label = "물타기 최고선",
initialValue = (tradeConfig.lowerAverageMinRate).toString(),
onSave = {
tradeConfig.lowerAverageMinRate = it.toDouble()
KisSession.saveTradeConfig()
},
helperText = "이것보다 작아야 삼"
)
}
Row(horizontalArrangement = Arrangement.SpaceEvenly) {
SettingInputField(
modifier = Modifier.weight(1.0f, true),
label = "물타기 기준 최소 보유 수량",
initialValue = (tradeConfig.lowerAverageTargetCount).toString(),
onSave = {
tradeConfig.lowerAverageTargetCount = it.toInt()
KisSession.saveTradeConfig()
},
helperText = "이거 이상 갖고 있어야 삼"
)
SettingInputField(
modifier = Modifier.weight(1.0f, true),
label = "물타기 개수",
initialValue = (tradeConfig.lowerAverageStockCount).toString(),
onSave = {
tradeConfig.lowerAverageStockCount = it.toInt()
KisSession.saveTradeConfig()
},
helperText = "1만큼 사고 팜"
)
}
} }
} }
} }
@ -716,9 +765,61 @@ fun CsvDropZone(
} }
//@OptIn(ExperimentalMaterialApi::class)
//@Composable
//fun SettingInputField(
// label: String,
// initialValue: String, // 💡 value -> initialValue 로 변경
// placeholder: String = "",
// helperText: String = "",
// onSave: (String) -> Unit // 💡 타자 칠 때마다가 아니라, 완료 시 저장하도록 콜백 변경
//) {
// // 💡 화면에 즉시 글자를 그려주기 위한 로컬 상태 (핵심 해결책)
// var localText by remember { mutableStateOf(initialValue) }
//
// Column(modifier = Modifier.fillMaxWidth()) {
// OutlinedTextField(
// value = localText,
// onValueChange = { localText = it }, // 타자 칠 때 화면 즉시 반영
// label = { Text(label, fontWeight = FontWeight.Bold) },
// placeholder = { Text(placeholder) },
// modifier = Modifier
// .fillMaxWidth()
// .onFocusChanged { focusState ->
// // 💡 포커스를 잃었을 때 (다른 칸을 클릭했을 때) 저장
// if (!focusState.isFocused) {
// onSave(localText)
// }
// },
// singleLine = true,
// keyboardOptions = KeyboardOptions(
// imeAction = ImeAction.Done,
// keyboardType = KeyboardType.Decimal
// ),
// keyboardActions = KeyboardActions(
// // 💡 모바일 키보드나 키보드에서 엔터(Done) 쳤을 때 저장
// onDone = {
// onSave(localText)
// }
// )
// )
//
// if (helperText.isNotEmpty()) {
// Spacer(modifier = Modifier.height(4.dp))
// Text(
// text = helperText,
// color = Color.Gray,
// fontSize = 11.sp,
// modifier = Modifier.padding(start = 4.dp)
// )
// }
// }
//}
@OptIn(ExperimentalMaterialApi::class) @OptIn(ExperimentalMaterialApi::class)
@Composable @Composable
fun SettingInputField( fun SettingInputField(
modifier: Modifier? = null,
label: String, label: String,
initialValue: String, // 💡 value -> initialValue 로 변경 initialValue: String, // 💡 value -> initialValue 로 변경
placeholder: String = "", placeholder: String = "",
@ -728,7 +829,7 @@ fun SettingInputField(
// 💡 화면에 즉시 글자를 그려주기 위한 로컬 상태 (핵심 해결책) // 💡 화면에 즉시 글자를 그려주기 위한 로컬 상태 (핵심 해결책)
var localText by remember { mutableStateOf(initialValue) } var localText by remember { mutableStateOf(initialValue) }
Column(modifier = Modifier.fillMaxWidth()) { Column(modifier = modifier ?: Modifier.fillMaxWidth()) {
OutlinedTextField( OutlinedTextField(
value = localText, value = localText,
onValueChange = { localText = it }, // 타자 칠 때 화면 즉시 반영 onValueChange = { localText = it }, // 타자 칠 때 화면 즉시 반영
@ -767,6 +868,7 @@ fun SettingInputField(
} }
} }
@Composable @Composable
fun SettingSwitchField( fun SettingSwitchField(
label: String, label: String,