package models sae import nxt Trade import nxt util Convert import org

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package models.sae
import nxt.Trade
import nxt.util.Convert
import org.joda.time.DateTime
object CandleStep extends Enumeration {
type CandleStep = Value
// val Minute = Value(60, "M1")
// val FiveMinutes = Value(300, "M5")
// val TenMinutes = Value(600, "M10")
// val FifteenMinutes = Value(900, "M15")
// val ThirtyMinutes = Value(1800, "M30")
val Hour = Value(3600, "H1")
val FourHours = Value(14400, "H4")
val Day = Value(86400, "D1")
val smallestCandle = Hour
def getScaleFactor(cs1: CandleStep.Value, cs2: CandleStep.Value): Int = cs2.id / cs1.id
def next(current: CandleStep.Value) = CandleStep.values.find(_.id > current.id)
}
case class Candle(assetId: Long, startTime: Long, step: CandleStep.Value,
open: Long, close: Long, high: Long, low: Long,
volumeQNT: Long, volumeNQT: Long) extends Ordered[Candle] {
def +(t: Trade): Candle = this.copy(close = t.getPriceNQT,
high = Math.max(this.high, t.getPriceNQT),
low = Math.min(this.low, t.getPriceNQT),
volumeQNT = this.volumeQNT + t.getQuantityQNT,
volumeNQT = this.volumeNQT + t.getQuantityQNT * t.getPriceNQT)
def +(that: Candle): Candle = Candle.merge(this, that)
def ++(those: Seq[Candle]): Candle = those.foldLeft(this) {
case (ac, c) => println(s"ac:$ac, c:$c"); ac + c
}
def ==(that: Candle): Boolean = this.assetId == that.assetId && this.startTime == that.startTime &&
this.step == that.step && this.open == that.open && this.close == that.close && this.high == that.high &&
this.low == that.low && this.volumeQNT == that.volumeQNT && this.volumeNQT == that.volumeNQT
def !=(that: Candle): Boolean = !(this == that)
override def toString = s"$assetId ${new DateTime(startTime)}($startTime) " +
s"($step) Open: $open High: $high Low: $low Close: $close; " +
s"Vol: $volumeQNT / $volumeNQT NQT"
override def compare(that: Candle): Int = {
require(this.step == that.step, "different steps - unable to compare")
this.startTime.compareTo(that.startTime)
}
}
object Candle extends CandleOperations with CandleOperationsMapDb {
def candleStartAndEnd(t: Long, st: CandleStep.Value) = (candleStart(t, st), nextCandleStart(t, st))
def candleStart(t: Long, st: CandleStep.Value): Long = (t / (1000l * st.id)) * (1000l * st.id)
def candleStart(t: Long, st: CandleStep.Value, shift: Int): Long = candleStart(t, st) + 1000l * st.id * shift
def nextCandleStart(t: Long, st: CandleStep.Value): Long = (t / (1000l * st.id) + 1) * (1000l * st.id)
def candlesBetween(start: Long, end: Long, st: CandleStep.Value): Long = Math.abs(end / (1000l * st.id) - start / (1000l * st.id) - 1)
def calculateEndTime(startTime: Long, step: CandleStep.Value): Long = startTime + 1000l * step.id
def apply(cs: List[Candle], step: CandleStep.Value) = {
require(cs.nonEmpty, "Empty list is not appropriate argument for Candle.apply()")
val h = cs.head
h.copy(step = step, startTime = Candle.candleStart(h.startTime, step)) ++ cs.tail
}
def apply(t: Trade, step: CandleStep.Value): Candle = {
val p = t.getPriceNQT
val st = Candle.candleStart(Convert.fromEpochTime(t.getTimestamp), step)
Candle(t.getAssetId, st, step, p, p, p, p, t.getQuantityQNT, t.getQuantityQNT * p)
}
def merge(first: Candle, second: Candle): Candle = {
require(second.startTime < Candle.nextCandleStart(first.startTime, first.step))
require(first.assetId == second.assetId,
s"Different assets cannot be merged($first, $second)")
require(first.startTime <= second.startTime,
s"First candle's startTime should be not greater than second($first +++ $second)")
val step = if (first.step >= second.step) first.step else second.step
Candle(first.assetId, first.startTime, step, first.open, second.close,
Math.max(first.high, second.high), Math.min(first.low, second.low),
first.volumeQNT + second.volumeQNT, first.volumeNQT + second.volumeNQT)
}
}