Warning: session_start(): open(/var/lib/php/sessions/sess_auns6lkio0tei11dk0mm325rhq, 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
set flag Reparse.
replace real with double.
replace Real with Double.
replace price with float.
srecord noeq MPM2(price[] ticker, long[] timestamps) {
// How much percentual slippage we have for each position on
// average plus the fees for the position.
// Obviously dependent on the market (trading site) we are using.
srecord Market(real adversity) {}
srecord Position(real openingTime, real direction) {}
srecord Juicer(real lossTolerance, real pullback) {}
abstract sclass Eye {
abstract real adviseDirection(Ticker ticker);
}
// only the first length items in data and timestamps are looked at
srecord noeq Ticker(price[] data, long[] timestamps, int length) {
Real currently() { ret lookback(0); }
Real lookback(real time) {
// TODO: binary search
int index = iceil(length-1-time);
ret index >= 0 ? (real) data[index] : null;
}
}
sclass LiveTicker extends Ticker {
void add(price value, long timestamp) {
data = addToArrayWithDoublingStrategy(data, time++, value);
timestamps = addToArrayWithDoublingStrategy(timestamps, time++, timestamp);
}
}
srecord SimpleEye(real time, real minMove) extends Eye {
real adviseDirection(Ticker ticker) {
Real currently = ticker.currently();
if (currently == null) ret 0;
Real before = ticker.lookback(time);
if (before == null) ret 0;
real move = currently/before*100-100;
if (abs(move) >= minMove) ret (real) sign(move);
ret 0;
}
}
real ticker(real time) { ret ticker[iceil(time)]; }
real openingPrice(Position p) { ret ticker(p.openingTime); }
// all "profit" functions return a positive or negative percent value
real profit(Position p, real time, Market m) {
ret (ticker(time)/openingPrice(p)*100-100)*p.direction - m.adversity;
}
Real closingTime(Position p, Juicer j, Market m) {
int time = iceil(p.openingTime);
real openingPrice = ticker(time);
real crest = -infinity();
while (time < ticker.length-1) {
real profit = profit(p, time, m);
crest = max(crest, profit);
if (profit < (profit < 0 ? -j.lossTolerance : crest-j.pullback))
ret (real) time;
++time;
}
null;
}
Real profit(Position p, Juicer j, Market m) {
Real closingTime = closingTime(p, j, m);
ret closingTime == null ? null : profit(p, closingTime, m);
}
// How profitable is it to open a position of any direction
// at a certain point in time?
Real profitability(real time, Juicer j, Market m) {
ret maxAllowingNull(
profit(new Position(time, -1), j, m),
profit(new Position(time, 1), j, m));
}
// eye can be null, then we just test the juicer
record noeq Backtest(Eye eye, Juicer j, Market m) extends Convergent {
Iterator openingTimes = new WeightlessShuffledIterator(intRangeList(ticker.length));
new Average profitPerPosition;
// TODO new Average averageHoldTime;
new Average positionsOpenedPercentage;
// percentage of positions closed before reaching the end of the ticker "tape"
new Average positionsClosedPercentage;
*(Juicer *j, Market *m) {}
// internal, don't call from outside. Use Convergent/Iterator methods
void step {
if (!openingTimes.hasNext())
ret with done = true;
int openingTime = openingTimes.next();
Real profit = null;
if (eye == null) // Test only juicer
profit = profitability(openingTime, j, m);
else {
// Test eye + juicer
real direction = eye.adviseDirection(new Ticker(ticker, openingTime));
positionsOpenedPercentage.addSample(direction != 0 ? 100 : 0);
if (direction != 0) {
Position position = new Position(openingTime, direction);
profit = profit(position, j, m);
}
}
positionsClosedPercentage.addSample(profit != null ? 100 : 0);
if (profit != null) {
profitPerPosition.addSample(profit);
//averageHoldTime.addSample(TODO);
} else // Profit for unclosed positions = 0
profitPerPosition.addSample(0);
value = profitPerPosition!;
}
}
}