Warning: session_start(): open(/var/lib/php/sessions/sess_olbkcmp2l6rl3u1ps2d305qq0q, 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
concept Chaser extends G22TradingStrategy {
// Juicer parameters
settable double maxLoss = 0.6;
settable double pullback = 0.4;
settable Opener opener;
// internal
new RunLengthCounter rlc;
OpenSignal openSignal;
// trigger = 0 to 1
record noeq CloseSignal(S reason, double trigger) {
double trigger() { ret trigger; }
}
// trigger = 0 to 1
record noeq OpenSignal(S reason, double trigger, int direction) {
double trigger() { ret trigger; }
}
persistable record Juicer(double maxLoss, double pullback) {
L closeSignals(Position p) {
new L signals;
double profit = p.profitBeforeLeverage();
// How close are we to the max closing time?
if (p.maxClosingTime() < infinity())
signals.add(new CloseSignal("Age", transformToZeroToOne(currentTime(), p.openingTime(), p.maxClosingTime())));
// How close are we to our loss limit?
if (profit < 0)
signals.add(new CloseSignal("Loss", doubleRatio(-profit, maxLoss));
else
// How close are we to the pullback limit?
signals.add(new CloseSignal("Profit", transformToZeroToOne(profit,
p.crest, p.crest-pullback));
ret signals;
}
}
abstract sclass Opener {
abstract OpenSignal openSignal();
abstract Opener cloneInto(Chaser strat);
}
record PreRunOpener(int minRunUp, int minRunDown) extends Opener {
Opener cloneInto(Chaser strat) {
ret strat.new PreRunOpener(minRunUp, minRunDown);
}
OpenSignal openSignal() {
int direction = Chaser.this.direction;
if (direction == 0) null;
int preRun = direction > 1 ? minRunUp : minRunDown;
int runLength = toInt(rlc.runLength());
printConcat(
"[",
formatLocalDateWithSeconds(currentTime()),
"] ",
"Runlength ",
runLength,
" price: ",
formatPriceX(currentPrice())
);
// TODO: even more precise signal
if (runLength >= preRun) print("Pre-run signal!");
ret new OpenSignal("RunLength " + runLength, doubleRatio(runLength, preRun), direction);
}
}
persistable class Position extends G22TradingStrategy.Position {
settable OpenSignal openSignal;
settable Juicer juicer;
// highest profit this position has reached
settable double crest = -infinity();
double maxClosingTime() {
ret infinity();
}
L closeSignals() {
ret juicer == null ?: juicer.closeSignals(this);
}
// >= 1 to close
// otherwise keep open
double closeSignal() {
CloseSignal strongestSignal = closeSignalWithDescription();
ret strongestSignal == null ? 0 : strongestSignal.trigger;
}
CloseSignal closeSignalWithDescription() {
ret highestBy(closeSignals(), signal -> signal.trigger);
}
void update {
crest = max(crest, profitBeforeLeverage());
}
}
LS status() {
ret listCombine(
"Max loss: " + formatDouble3(maxLoss) + "%" + ", pullback: " + formatDouble3(pullback) + "%",
"Opener: " + opener,
"Run length: " + rlc.runLength(),
openSignal == null ?: "Open signal: " + openSignal,
super.status()
);
}
Position openPosition(OpenSignal openSignal) {
int direction = openSignal.direction;
if (l(positionsInDirection(direction)) > 0) {
log("Not opening a second position in direction " + direction);
null;
}
nextStep();
new Position p;
p.openSignal(openSignal);
p.juicer(new Juicer(maxLoss, pullback));
ret openPosition(p, direction);
}
void start {
if (started()) fail("Already started");
if (currentPrice() == 0)
ret with primed(true);
// Save starting price, create price cells & digitizer
startingPrice = currentPrice();
var priceCells = makePriceCells(startingPrice);
digitizer = new PriceDigitizer2(priceCells);
digitizer.verbose(verbose);
log("Starting CHASER at " + startingPrice + " +/- " + cellSize);
}
void price(double price) {
if (currentPrice == price) ret;
currentPrice = price;
if (!started()) {
if (primed) {
primed(false);
start();
nextStep();
beforeStep();
afterStep();
}
ret;
}
digitizer.digitize(price);
direction = sign(digitizedPrice()-lastDigitizedPrice());
if (direction != 0) lastDirection = direction;
if (started())
step();
}
swappable void step {
// Update positions with new price
for (p : openPositions()) {
cast p to Position;
p.update();
}
// Find positions to close
L toClose = new L;
for (p : openPositions()) {
cast p to Position;
CloseSignal signal = p.closeSignalWithDescription();
if (signal != null && signal.trigger() >= 1) {
p.closeReason(signal);
log("Closing position because " + signal + ": " + p);
toClose.add(p);
}
}
if (nempty(toClose)) {
nextStep();
closePositions(toClose);
afterStep();
}
beforeStep();
double p1 = lastDigitizedPrice();
double p2 = digitizedPrice();
direction = sign(p2-p1);
if (direction != 0)
rlc.add(direction);
openSignal = makeOpenSignal();
if (openSignal != null && openSignal.trigger >= 1)
openPosition(openSignal);
afterStep();
}
swappable OpenSignal makeOpenSignal() {
ret opener == null ?: opener.openSignal();
}
swappable void beforeStep() {}
S baseToString() {
ret colonCombine(
_conceptID() == 0 ? "" : "Strategy " + _conceptID(),
"Chaser profit " + formatMarginProfit(coinProfit()));
}
void reset :: before {
resetFields(this, "rlc openSignal");
}
Chaser emptyClone() {
Chaser strat = cast super.emptyClone();
ret strat.opener(opener?.cloneInto(strat));
}
// Chaser.toString
toString { ret baseToString(); }
}