Bankroll report update

feature/top10
Laurent 7 years ago
parent 8f3bd78f49
commit a272986b6c
  1. 14
      app/src/main/java/net/pokeranalytics/android/calculus/Stat.kt
  2. 47
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollCalculator.kt
  3. 165
      app/src/main/java/net/pokeranalytics/android/calculus/bankroll/BankrollReport.kt
  4. 11
      app/src/main/java/net/pokeranalytics/android/calculus/interfaces/Datable.kt
  5. 15
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Dated.kt
  6. 7
      app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt
  7. 132
      app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt

@ -14,6 +14,7 @@ import net.pokeranalytics.android.util.extensions.formatted
import net.pokeranalytics.android.util.extensions.formattedHourlyDuration import net.pokeranalytics.android.util.extensions.formattedHourlyDuration
import net.pokeranalytics.android.util.extensions.toCurrency import net.pokeranalytics.android.util.extensions.toCurrency
import java.util.* import java.util.*
import kotlin.math.exp
class StatFormattingException(message: String) : Exception(message) { class StatFormattingException(message: String) : Exception(message) {
@ -128,6 +129,19 @@ enum class Stat : RowRepresentable {
return netBB / numberOfHands * 100 return netBB / numberOfHands * 100
} }
fun riskOfRuin(hourlyRate: Double, hourlyStandardDeviation: Double, bankrollValue: Double) : Double? {
if (bankrollValue <= 0.0) {
return null
}
val numerator = -2 * hourlyRate * bankrollValue
val denominator = Math.pow(hourlyStandardDeviation, 2.0)
val ratio = numerator / denominator
return exp(ratio)
}
} }
override val resId: Int? override val resId: Int?

@ -1,9 +1,14 @@
package net.pokeranalytics.android.calculus.bankroll package net.pokeranalytics.android.calculus.bankroll
import io.realm.Realm import io.realm.Realm
import net.pokeranalytics.android.calculus.Calculator
import net.pokeranalytics.android.calculus.ComputableGroup
import net.pokeranalytics.android.calculus.ComputedResults
import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.model.realm.Filter import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.model.realm.Transaction
import net.pokeranalytics.android.model.realm.TransactionType
class BankrollCalculator { class BankrollCalculator {
@ -14,22 +19,58 @@ class BankrollCalculator {
val realm = Realm.getDefaultInstance() val realm = Realm.getDefaultInstance()
val report = BankrollReport(setup) val report = BankrollReport(setup)
val useRatedValues = (setup.bankroll == null)
val sessions = Filter.queryOn<Session>(realm, setup.queryConditions) val queryConditions = setup.queryConditions
val transactions = Filter.queryOn<Transaction>(realm, setup.queryConditions) val sessions = Filter.queryOn<Session>(realm, queryConditions)
val transactions = Filter.queryOn<Transaction>(realm, queryConditions)
val sessionsNet = sessions.sum("result.net")
val transactionsNet = transactions.sum("value") val transactionsNet = transactions.sum("value")
report.addDatedItems(transactions)
transactions.forEach { transactions.forEach {
report.addTransaction(it) report.addTransaction(it)
} }
val group = ComputableGroup("", queryConditions, listOf(Stat.NET_RESULT, Stat.HOURLY_RATE, Stat.STANDARD_DEVIATION_HOURLY))
val result = Calculator.compute(realm, group, Calculator.Options())
result.computedStat(Stat.NET_RESULT)?.let {
report.netResult = it.value
}
report.transactionsNet = transactionsNet.toDouble()
this.computeRiskOfRuin(report, result)
val depositType = TransactionType.getByValue(TransactionType.Value.DEPOSIT, realm)
report.transactionBuckets[depositType.id]?.let { bucket ->
report.depositTotal = bucket.transactions.sumByDouble { it.amount }
}
val withdrawalType = TransactionType.getByValue(TransactionType.Value.WITHDRAWAL, realm)
report.transactionBuckets[withdrawalType.id]?.let { bucket ->
report.withdrawalTotal = bucket.transactions.sumByDouble { it.amount }
}
realm.close() realm.close()
return report return report
} }
fun computeRiskOfRuin(report: BankrollReport, results: ComputedResults) {
val hourlyRate = results.computedStat(Stat.HOURLY_RATE)?.value
val hourlyStandardDeviation = results.computedStat(Stat.STANDARD_DEVIATION_HOURLY)?.value
if (hourlyRate != null && hourlyStandardDeviation != null) {
report.riskOfRuin = Stat.riskOfRuin(hourlyRate, hourlyStandardDeviation, report.total)
}
}
} }
} }

@ -1,12 +1,109 @@
package net.pokeranalytics.android.calculus.bankroll package net.pokeranalytics.android.calculus.bankroll
import net.pokeranalytics.android.calculus.interfaces.DatableValue
import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.DatedValue
import net.pokeranalytics.android.model.realm.Bankroll import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.Transaction import net.pokeranalytics.android.model.realm.Transaction
import java.util.* import java.util.*
import kotlin.collections.HashMap import kotlin.collections.HashMap
class BankrollReport(setup: BankrollReportSetup) {
/**
* The setup used to compute the report
*/
var setup: BankrollReportSetup = setup
/**
* The value of the bankroll
*/
var total: Double = 0.0
private set
/**
* The net result from poker computables
*/
var netResult: Double = 0.0
set(value) {
field = value
total = this.netResult + this.transactionsNet
}
/**
* The net result from transactions
*/
var transactionsNet: Double = 0.0
set(value) {
field = value
total = this.netResult + this.transactionsNet
}
/**
* The sum of all deposits
*/
var depositTotal: Double = 0.0
set(value) {
field = value
this.netBanked = this.depositTotal + this.withdrawalTotal
}
/**
* The sum of all withdrawals
*/
var withdrawalTotal: Double = 0.0
set(value) {
field = value
this.netBanked = this.depositTotal + this.withdrawalTotal
}
/**
* The difference between withdrawals and deposits
*/
var netBanked: Double = 0.0
private set
/**
* The risk of ruin
*/
var riskOfRuin: Double? = null
var transactions: List<Transaction> = mutableListOf()
private set
var transactionBuckets: HashMap<String, TransactionBucket> = HashMap()
private set
var evolutionPoints: MutableList<BRGraphPoint> = mutableListOf()
var evolutionItems: MutableList<DatedValue> = mutableListOf()
private set
fun addDatedItems(items: Collection<DatedValue>) {
this.evolutionItems.addAll(items)
}
fun addTransaction(transaction: Transaction) {
transaction.type?.let { type ->
var bucket = this.transactionBuckets[type.id]
if (bucket == null) {
val b = TransactionBucket(this.setup.bankroll == null)
this.transactionBuckets[type.id] = b
bucket = b
}
bucket.addTransaction(transaction)
} ?: run {
throw Exception("Transaction has no type")
}
}
}
/** /**
* A class describing the parameters required to launch a bankroll report * A class describing the parameters required to launch a bankroll report
* *
@ -60,72 +157,8 @@ class TransactionBucket(useRate: Boolean = false) {
} }
class BRGraphPoint { data class BRGraphPoint(var value: Double = 0.0, var date: Date? = null, var data: Any? = null) {
var value: Double = 0.0
var variation: Double = 0.0 var variation: Double = 0.0
var date: Date? = null
} }
class BankrollReport(setup: BankrollReportSetup) {
/**
* The setup used to compute the report
*/
var setup: BankrollReportSetup = setup
/**
* The value of the bankroll
*/
var total: Double = 0.0
private set
/**
* The net result from poker computables
*/
var netResult: Double = 0.0
private set
/**
* The difference between withdrawals and deposits
*/
var netBanked: Double = 0.0
private set
/**
* The risk of ruin
*/
var riskOfRuin: Double = 0.0
private set
var transactions: List<Transaction> = mutableListOf()
private set
var transactionBuckets: HashMap<String, TransactionBucket> = HashMap()
var evolutionPoints: Array<BRGraphPoint> = arrayOf()
var evolutionItems: Array<DatableValue> = arrayOf()
fun addTransaction(transaction: Transaction) {
transaction.type?.let { type ->
var bucket = this.transactionBuckets[type.id]
if (bucket == null) {
val b = TransactionBucket(this.setup.bankroll == null)
this.transactionBuckets[type.id] = b
bucket = b
}
bucket.addTransaction(transaction)
} ?: run {
throw Exception("Transaction has no type")
}
}
}

@ -1,11 +0,0 @@
package net.pokeranalytics.android.calculus.interfaces
import java.util.*
interface Datable {
var date: Date
}
interface DatableValue : Datable {
var value: Double
}

@ -0,0 +1,15 @@
package net.pokeranalytics.android.model.interfaces
import java.util.*
interface Dated {
var date: Date
}
interface DatedValue : Dated {
var amount: Double
}

@ -7,6 +7,7 @@ import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.filter.Filterable import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.DatedValue
import net.pokeranalytics.android.model.interfaces.Manageable import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
@ -17,7 +18,7 @@ import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, Filterable { open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSource, RowRepresentable, Filterable, DatedValue {
companion object { companion object {
val rowRepresentation: List<RowRepresentable> by lazy { val rowRepresentation: List<RowRepresentable> by lazy {
@ -42,10 +43,10 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
var bankroll: Bankroll? = null var bankroll: Bankroll? = null
// The amount of the transaction // The amount of the transaction
var amount: Double = 0.0 override var amount: Double = 0.0
// The date of the transaction // The date of the transaction
var date: Date = Date() override var date: Date = Date()
// The type of the transaction // The type of the transaction
var type: TransactionType? = null var type: TransactionType? = null

@ -14,65 +14,77 @@ import kotlin.collections.ArrayList
open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable { open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentableDataSource, RowRepresentable {
companion object {
val rowRepresentation : List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TransactionTypeRow.values())
rows
}
}
@PrimaryKey
override var id = UUID.randomUUID().toString()
// The name of the transaction type
override var name: String = ""
// Whether or not the amount is added, or subtracted to the bankroll total
var additive: Boolean = false
// Whether or not the type can be deleted by the user
var lock: Boolean = false
// The predefined kind, if necessary, like: Withdrawal, deposit, or tips
var kind: Int? = null
override fun getDisplayName(): String {
return this.name
}
override fun adapterRows(): List<RowRepresentable>? {
return TransactionType.rowRepresentation
}
override fun stringForRow(row: RowRepresentable): String {
return when (row) {
SimpleRow.NAME -> this.name
else -> return super.stringForRow(row)
}
}
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return row.editingDescriptors(mapOf("defaultValue" to this.name))
}
override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) {
SimpleRow.NAME -> this.name = value as String? ?: ""
}
}
override fun isValidForDelete(realm: Realm): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun getFailedDeleteMessage(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
enum class TransactionKind { enum class Value {
WITHDRAWAL, WITHDRAWAL,
DEPOSIT DEPOSIT,
BONUS
}
companion object {
val rowRepresentation: List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(SimpleRow.NAME)
rows.addAll(TransactionTypeRow.values())
rows
}
fun getByValue(value: Value, realm: Realm): TransactionType {
val type = realm.where(TransactionType::class.java).equalTo("kind", value.ordinal).findFirst()
type?.let {
return it
}
throw IllegalStateException("Transaction type ${value.name} should exist in database!")
}
}
@PrimaryKey
override var id = UUID.randomUUID().toString()
// The name of the transaction type
override var name: String = ""
// Whether or not the amount is added, or subtracted to the bankroll total
var additive: Boolean = false
// Whether or not the type can be deleted by the user
var lock: Boolean = false
// The predefined kind, if necessary, like: Withdrawal, deposit, or tips
var kind: Int? = null
override fun getDisplayName(): String {
return this.name
}
override fun adapterRows(): List<RowRepresentable>? {
return rowRepresentation
}
override fun stringForRow(row: RowRepresentable): String {
return when (row) {
SimpleRow.NAME -> this.name
else -> return super.stringForRow(row)
}
}
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return row.editingDescriptors(mapOf("defaultValue" to this.name))
}
override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) {
SimpleRow.NAME -> this.name = value as String? ?: ""
}
}
override fun isValidForDelete(realm: Realm): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override fun getFailedDeleteMessage(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
} }

Loading…
Cancel
Save