Warning: session_start(): open(/var/lib/php/sessions/sess_nrlmkb4a69jkahngllu3i11h11, O_RDWR) failed: No space left on device (28) in /var/www/tb-usercake/models/config.php on line 51
Warning: session_start(): Failed to read session data: files (path: /var/lib/php/sessions) in /var/www/tb-usercake/models/config.php on line 51
persistable sclass TradingCandleMaker {
settable double candleLength = 1; // in minutes
settable int maxCandles = Int.MAX_VALUE;
settable double alignment; // shift in seconds
// candleLength = granularity in minutes
*(double *candleLength) {}
L candles = syncL();
TradingCandle currentCandle() { ret last(candles); }
TradingCandle completedCandle() { ret nextToLast(candles); }
bool isEmpty() { ret main isEmpty(candles); }
Timestamp endTime() {
var candle = last(candles);
ret candle?.endTime();
}
double lastPrice() { ret currentCandle().end(); }
void add(double price, long timestamp) {
var ts = new Timestamp(timestamp);
int safety = 0;
while (currentCandle() != null) {
if (safety++ >= 1000) {
warn("TradingCandleMaker safety warning");
break;
}
Timestamp maxEnd = maxEndOfCurrentCandle();
if (!greaterOrEq(ts, maxEnd)) break;
currentCandle().endTime(maxEnd);
double lastPrice = lastPrice();
newCandle();
var cc = currentCandle();
cc.addValue(lastPrice, maxEnd);
if (cc.projectedEndTime() == 0)
cc.projectedEndTime(maxEndOfCurrentCandle().unixDate());
}
if (empty(candles)) newCandle();
currentCandle().addValue(price, ts);
}
void newCandle {
var c = currentCandle();
c?.ongoing(false).projectedEndTime(0);
candles.add(new TradingCandle().ongoing(true));
dropOldCandles();
}
// returns number of entries processed
int addTickerSequence aka add aka feed(TickerSequence ts) {
int entriesProcessed = 0;
Timestamp endTime = endTime();
// TODO: Cut off beginning of ticker according to maxCandles
if (endTime != null)
ts = ts.subSequenceFromTimestamp(endTime().unixDate()+1);
int n = l(ts);
for i to n: {
add(ts.prices.get(i), ts.timestamps.get(i));
++entriesProcessed;
}
if (!isEmpty() && ts.lastTickerEvent > currentCandle().endTime().toLong()) {
add(ts.lastPrice(), ts.lastTickerEvent);
++entriesProcessed;
}
ret entriesProcessed;
}
Timestamp maxEndOfCurrentCandle() {
long time = currentCandle().startTime.toLong();
long align = secondsToMS(alignment);
time = roundUpTo(secondsToMS(candleLength*60), time+align+1)-align;
ret new Timestamp(time);
}
/*bool needNewCandle(long timestamp) {
ret empty(candles)
|| timestamp >= maxEndOfCurrentCandle().unixDate();
}*/
void dropOldCandles {
if (l(candles) > maxCandles)
removeSubList(candles, 0, l(candles)-maxCandles);
}
L candles aka get() { ret cloneList(candles); }
L liveCandles() { ret candles; }
// only complete candles (cut off last candle and also first candle if incomplete)
L completeCandles() {
synchronized(mutex()) {
int startIdx = isComplete(first(candles)) ? 0 : 1;
ret cloneSubList(candles, startIdx, l(candles)-1);
}
}
// We just synchronize on the candles list
O mutex() { ret candles; }
bool isComplete(TradingCandle candle) {
ret candle != null && candle.durationInSeconds() == candleLength*60;
}
toString { ret "Candle maker " + formatDouble(candleLength) + "m, " + nCandles(candles); }
L get(TickerSequence ticker) {
feed(ticker);
ret candles();
}
selfType granularity(double minutes) {
ret candleLength(minutes);
}
}