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,72 +1,12 @@
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
/**
* A class describing the parameters required to launch a bankroll report
*
*/
class BankrollReportSetup(val bankroll: Bankroll?, val from: Date? = null, val to: Date? = null) {
val queryConditions: List<QueryCondition>
get() {
val conditions = mutableListOf<QueryCondition>()
this.bankroll?.let {
val bankrollCondition = QueryCondition.AnyBankroll()
bankrollCondition.setObject(it)
conditions.add(bankrollCondition)
}
this.from?.let {
val fromCondition = QueryCondition.StartedFromDate()
fromCondition.singleValue = it
conditions.add(fromCondition)
}
this.to?.let {
val toCondition = QueryCondition.StartedToDate()
toCondition.singleValue = it
conditions.add(toCondition)
}
return conditions
}
}
class TransactionBucket(useRate: Boolean = false) {
var transactions: MutableList<Transaction> = mutableListOf()
private set
var total: Double = 0.0
private set
var useRate: Boolean = useRate
private set
fun addTransaction(transaction: Transaction) {
this.transactions.add(transaction)
var rate = 1.0
if (this.useRate) {
rate = transaction.bankroll?.currency?.rate ?: 1.0
}
val ratedAmount = rate * transaction.amount
this.total += ratedAmount
}
}
class BRGraphPoint {
var value: Double = 0.0
var variation: Double = 0.0
var date: Date? = null
}
class BankrollReport(setup: BankrollReportSetup) { class BankrollReport(setup: BankrollReportSetup) {
@ -85,7 +25,37 @@ class BankrollReport(setup: BankrollReportSetup) {
* The net result from poker computables * The net result from poker computables
*/ */
var netResult: Double = 0.0 var netResult: Double = 0.0
private set 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 * The difference between withdrawals and deposits
@ -96,16 +66,21 @@ class BankrollReport(setup: BankrollReportSetup) {
/** /**
* The risk of ruin * The risk of ruin
*/ */
var riskOfRuin: Double = 0.0 var riskOfRuin: Double? = null
private set
var transactions: List<Transaction> = mutableListOf() var transactions: List<Transaction> = mutableListOf()
private set private set
var transactionBuckets: HashMap<String, TransactionBucket> = HashMap() var transactionBuckets: HashMap<String, TransactionBucket> = HashMap()
private set
var evolutionPoints: MutableList<BRGraphPoint> = mutableListOf()
var evolutionItems: MutableList<DatedValue> = mutableListOf()
private set
var evolutionPoints: Array<BRGraphPoint> = arrayOf() fun addDatedItems(items: Collection<DatedValue>) {
var evolutionItems: Array<DatableValue> = arrayOf() this.evolutionItems.addAll(items)
}
fun addTransaction(transaction: Transaction) { fun addTransaction(transaction: Transaction) {
@ -127,5 +102,63 @@ class BankrollReport(setup: BankrollReportSetup) {
} }
}
/**
* A class describing the parameters required to launch a bankroll report
*
*/
class BankrollReportSetup(val bankroll: Bankroll?, val from: Date? = null, val to: Date? = null) {
val queryConditions: List<QueryCondition>
get() {
val conditions = mutableListOf<QueryCondition>()
this.bankroll?.let {
val bankrollCondition = QueryCondition.AnyBankroll()
bankrollCondition.setObject(it)
conditions.add(bankrollCondition)
}
this.from?.let {
val fromCondition = QueryCondition.StartedFromDate()
fromCondition.singleValue = it
conditions.add(fromCondition)
}
this.to?.let {
val toCondition = QueryCondition.StartedToDate()
toCondition.singleValue = it
conditions.add(toCondition)
}
return conditions
}
}
class TransactionBucket(useRate: Boolean = false) {
var transactions: MutableList<Transaction> = mutableListOf()
private set
var total: Double = 0.0
private set
var useRate: Boolean = useRate
private set
fun addTransaction(transaction: Transaction) {
this.transactions.add(transaction)
var rate = 1.0
if (this.useRate) {
rate = transaction.bankroll?.currency?.rate ?: 1.0
}
val ratedAmount = rate * transaction.amount
this.total += ratedAmount
}
}
data class BRGraphPoint(var value: Double = 0.0, var date: Date? = null, var data: Any? = null) {
var variation: Double = 0.0
} }

@ -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