Die Werte des Währungspaars fasse ich in einer Klasse zusammen:
/* base/counter currency pair class and its companion object */
object BCPair extends XMLTools {
def apply(b: Double, c: Double) = new BCPair(b, c)
def apply(bcp: BCPair) = new BCPair(bcp.b, bcp.c)
def apply() = new BCPair()
def apply(xml: Elem) = new BCPair( b = tDbl(xml \ "base"),
c = tDbl(xml \ "counter"))
val zero = BCPair(0, 0)
val error = BCPair(-1, -1)
}
class BCPair (var b: Double = 0, var c: Double = 0) {
/* data manipulation */
def set(in: (Double, Double)) {b = in._1; c = in._2}
def set(bcp: BCPair) {b = bcp.b; c = bcp.c}
def set(bb: Double, cc: Double) {b = bb; c = cc}
/* abs, rounding*/
def abs = BCPair(scala.math.abs(b), scala.math.abs(c))
def rnd(decs: DecSet) = BCPair( b = decs.rndBase(b),
c = decs.rndCntr(c) )
/* check conditions */
def cndAND (f: Double => Boolean) = f(b) && f(c)
def cndOR (f: Double => Boolean) = f(b) || f(c)
/* standard operators */
def + (that: BCPair) = new BCPair(this.b + that.b, this.c + that.c)
def - (that: BCPair) = new BCPair(this.b - that.b, this.c - that.c)
def * (d: Double) = new BCPair(this.b * d, this.c * d)
def / (d: Double) = new BCPair(this.b / d, this.c / d)
def isEqual (that: BCPair) = this.b == that.b && this.c == that.c
/* calc the rate: i * r = c <=> r = c / i */
def calcRate = if (b != 0) scala.math.abs(c / b) else -1D
/* delivers min/max values of both entries */
def mergeMax(that: BCPair) = BCPair( b = tools.max(b, that.b),
c = tools.max(c, that.c) )
def mergeMinPos(that: BCPair) = {
def oz(x: Double, y: Double) = (x > 0, y > 0) match {
case (true, true) => tools.min(x, y)
case (false, true) => y
case (true, false) => x
case (false, false) => 0D }
BCPair(b = oz(b, that.b), c = oz(c, that.c)) }
/* formatted output */
def print = "base:" + b + " cntr:" + c
def toStr( bcu: BCUnits, decB: Int, decC: Int) =
fmtAmt(b, bcu.b, decB) + " | " + fmtAmt(c, bcu.c, decC)
def toStr( bcu: BCUnits, fmtB: Double => String, fmtC: Double => String) =
fmtAmt(b, bcu.b, fmtB) + " | " + fmtAmt(c, bcu.c, fmtC)
def toStr(decs: Decimals, units: BCUnits): String =
toStr(bcu = units, decB = decs.decBase, decC = decs.decCntr)
def toStr(decs: DecSet, units: BCUnits): String =
toStr(bcu = units, decB = decs.decBase, decC = decs.decCntr)
/* XML output */
def toXML = fXML(b.toString, c.toString)
def toXML(decs: DecSet) = fXML(decs.fmtBase(b), decs.fmtCntr(c))
private def fXML(sb: String, sc: String) =
/* helper functions */
private def cLen(s: String) = {if (s.indexOf("-") < 0) 1 else 0} + s.length
private def fmtAmt(v: Double, unit: String, fmt: Double => String) = {
val s = fmt(v)
tools.pst( cLen(s), s) + " " + unit }
private def fmtAmt(v: Double, unit: String, dec: Int) = {
val s = tools.format(v, dec)
tools.pst( cLen(s), s) + " " + unit }
}
Auch etwas länglich, aber ich kann jetzt ...
// ... hiermit beispielsweise zwei Handelergebnisse aufsummieren.
val a = BCPair(b = 1, c = 100)
val b = BCPair(2, -30)
val c = a + b // wäre hier BCPair(3, 70)
// und mit der toStr-Methode ins Logfile schreiben
log.trade("trade result -> " + c.toStr(decs.calc, ...)
// oder testen, ob beide werte positiv sind
if (c.cndAND (v => v > 0)) { ... }
Das erspart auch Arbeit bei der Deklaration von Klassen, beispielsweise einer Klasse, die die Handelsresultate aufnimmt:
/* the IFExTradeData class itself */
case class IFExTradeData ( dbid: Int,
typ: TTrdDir.TTrdDir,
bcu: BCUnits,
timestamp: Long,
tradeid: Long,
orderid: Long,
resultWOF: BCPair,
resultWF: BCPair,
fees: BCPair,
rateWOF: Double,
rateWF: Double) extends IFExDataBase(dbid) {
/* for error messages */
override val errorClassName = "IFExTradeData"
/* check on equality */
def isEqual(that: IFExTradeData) = dbid == that.dbid &&
typ == that.typ &&
bcu == that.bcu &&
timestamp == that.timestamp &&
tradeid == that.tradeid &&
orderid == that.orderid &&
resultWOF.isEqual(that.resultWOF) &&
resultWF.isEqual(that.resultWF) &&
fees.isEqual(that.fees) &&
rateWOF == that.rateWOF &&
rateWF == that.rateWF
/* type to string */
def typStr = TTrdDir.toStr(typ)
/* delivers a copy with setting of a changed database id */
def copyWithDBID(id: Int) = copy(dbid = id)
/* build log string */
def toLog( indent: String, decs: DecSet) = {
def strBCP(bcp: BCPair) =
bcp.toStr( bcu, decs.fmtBase _, decs.fmtCntr _)
List( "TRADEDATA",
indent + "typ : " + typStr.toUpperCase,
indent + "orderid : " + orderid,
indent + "tradeid : " + tradeid,
indent + "timestamp : " + tools.dateToString(timestamp),
indent + "res w/o fee : " + strBCP(resultWOF),
indent + "res w fee : " + strBCP(resultWF),
indent + "fees : " + strBCP(fees),
indent + "rate w/o fee : " + fmtValue(rateWOF, decs.fmtRate),
indent + "rate w fee : " + fmtValue(rateWF, decs.fmtRate)).mkString("\n") }
/* creates a name/value map for database storing */
override def dbValues(tableName: String, secID: Int) =
Map( ("TableName", tableName),
("PrimaryKeyName", pkName),
("PrimaryKeyValue", dbid),
("secid", secID),
("typ", typStr),
("base", bcu.b),
("cntr", bcu.c),
("timestamp", timestamp),
("tradeid", tradeid),
("orderid", orderid),
("ratewof", rateWOF),
("ratewf", rateWF)) ++
mapBCP("resultwof", resultWOF) ++
mapBCP("resultwf", resultWF) ++
mapBCP("fees", fees)
/* stores to database, only if not exists */
override def store(db: Database, table: String, secID: Int) =
/* checks if dataset already exists */
db.selectFromDB( table, "where tradeid=" + tradeid, "") match {
/* dataset retrieved --> no action */
case rs: ResultSet if rs.next() => rs.close()
/* no dataset found --> store */
case rs: ResultSet => super.store(db, table, secID); rs.close()
case _ => super.store(db, table, secID) }
}
Wieder alles etwas länglich, aber sehr nützlich für die einheitliche Behandlung der Daten von verschiedenen Exchanges.