Warning: session_start(): open(/var/lib/php/sessions/sess_ilao65gh7mur9gc51cirsovs87, 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.
// Persistence:
// MPM3.BotInActionis persistable which in turn means
// MPM3, Position, Juicer and other classes are also persistable.
// We don't persist Position.ticker for space reasons though.
persistable sclass MPM3 {
// MARKET (trading platform)
// 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.
settable real marketAdversity = 0.12 + 0.08;
// SELF-IMPOSED LIMITS
// How long a position can be held at the longest
gettable real maxPositionDuration = minutesToMS(60);
real minimalMaxDuration() { ret minutesToMS(1); }
// correct user if they are stupid
selfType maxPositionDuration(real whatUserWants) {
maxPositionDuration = max(whatUserWants, minimalMaxDuration());
this;
}
// POSITIONS
// An open(ed) position
persistable record Position(transient TickerSequence ticker, real openingTime, real direction) {
// Set this to a non-null value if this is a real position on a real market
settable O irlInfo;
real openingPrice() { ret ticker.priceAtTimestamp(openingTime); }
real profitAtTime(real time) {
ret (ticker.priceAtTimestamp(time)/openingPrice()*100-100)*direction - marketAdversity;
}
real openingTime() { ret openingTime; }
}
persistable record LivePosition(Position p) {
settable Juicer juicer;
// highest profit this position has reached
settable real crest = -infinity();
settable real time; // time we last updated this position
settable real profit; // expected profit at that time
simplyCached real maxClosingTime() {
ret min(p.openingTime+maxPositionDuration, p.ticker.endTime());
}
bool shouldClose() {
ret time >= maxClosingTime()
|| profit < (profit < 0 ? -juicer.lossTolerance : crest-juicer.pullback);
}
// >= 1 to close
// otherwise keep open
real closeSignal() {
// How close are we to the max closing time?
real signal = transformToZeroToOne(time, p.openingTime(), maxClosingTime());
// How close are we to our loss limit?
if (profit < 0)
signal = max(signal, doubleRatio(-profit, juicer.lossTolerance));
else
// How close are we to the pullback limit?
signal = max(signal, transformToZeroToOne(profit,
crest, crest-juicer.pullback));
ret signal;
}
}
record ClosedPosition(Position p, real closingTime) {
real profit = Double.NaN;
MPM3 mpm() { ret MPM3.this; }
TickerSequence ticker() { ret p.ticker; }
real direction() { ret p.direction; }
real openingTime() { ret p.openingTime; }
real closingTime() { ret closingTime; }
real openingPrice() { ret p.openingPrice(); }
real closingPrice() { ret ticker().priceAtTimestamp(closingTime); }
real duration() { ret closingTime()-openingTime(); }
real profit() {
if (isNaN(profit))
profit = p.profitAtTime(closingTime);
ret profit;
}
}
// Wrappers for L and L
// (maybe not needed?)
sclass LivePositions {
L positions = syncL();
}
sclass ClosedPositions {
L positions = syncL();
}
// BOT (Eye + Juicer)
persistable srecord Juicer(real lossTolerance, real pullback) {}
abstract sclass Eye {
// returns -1 to open short, 1 to open long
// and anything in between to do nothing
abstract real adviseDirection(TickerPoint tickerPoint);
}
// time = ms to look back
srecord SimpleEye(long lookbackTime, real minMove) extends Eye {
real adviseDirection(TickerPoint tickerPoint) {
real move = calculateMove(tickerPoint);
if (isNaN(move)) ret 0;
real relativeMove = doubleRatio(move, minMove);
ret clamp(relativeMove, -1, 1);
}
real calculateMove(TickerPoint tickerPoint) {
if (!tickerPoint.canLookback(lookbackTime)) ret Double.NaN;
real currentPrice = tickerPoint.currentPrice();
real before = tickerPoint.lookback(lookbackTime);
ret currentPrice/before*100-100;
}
}
srecord TradingBot(Eye eye, Juicer juicer) {
*(real lookbackMinutes, real minMove, real maxLoss, real pullback) {
eye = new SimpleEye(minutesToMS(lookbackMinutes), minMove);
juicer = new Juicer(maxLoss, pullback);
}
}
sclass BotInAction {
TradingBot botConfiguration;
// Both open and closed positions may be purely simulated or
// mirrors of positions actually made IRL (see Position.irlInfo).
new ListWithChangeListeners openPositions;
new ListWithChangeListeners closedPositions;
}
ClosedPosition runJuicer(Position p, Juicer j) {
TickerSequence ticker = p.ticker;
long time = lround(p.openingTime);
double maxClosingTime = min(time+maxPositionDuration, ticker.endTime());
real crest = -infinity();
while (time < maxClosingTime) {
time = ticker.nextTimestamp(time);
real profit = p.profitAtTime(time);
crest = max(crest, profit);
if (profit < (profit < 0 ? -j.lossTolerance : crest-j.pullback))
break;
}
ret new ClosedPosition(p, time);
}
record noeq Backtest(TickerSequence ticker, TradingBot bot) extends Convergent {
new L closedPositions;
long time;
double latestAllowedOpeningTime() {
ret ticker.endTime()-maxPositionDuration;
}
void step {
if (time == 0) time = ticker.startTime();
if (time > latestAllowedOpeningTime())
ret with done = true;
TickerPoint tickerPoint = new(ticker, time);
real direction = bot.eye.adviseDirection(tickerPoint);
if (abs(direction) < 1)
// No position to open, move to next timestamp
time = ticker.nextTimestamp(time);
else {
// We have a position to open
var position = new Position(ticker, time, direction);
// Ask juicer when to close position
var closedPosition = runJuicer(position, bot.juicer);
// Add to record of positions made
closedPositions.add(closedPosition);
// Move on to next timestamp
time = ticker.nextTimestamp(closedPosition.closingTime);
}
}
}
}