Warning: session_start(): open(/var/lib/php/sessions/sess_n5h71h9pa4trn601k2pjetgres, 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
// Design notes.
//
// NG stands for "Non-Garbage"
//
// !Design notes end
sclass NGSStrategy2 extends G22CandleBasedStrategyWithTrailingStop {
settable int period1 = 100; // the "slow" MA
settable int period2 = 5; // the fast MA (for opening)
settable double maMinMove = 0.07; // MA trend threshold
settable double maMoveHysteresis = 0.01; // MA trend non-oscillation threshold
settable Side longSide = new(1);
settable Side shortSide = new(-1);
// per-run fields
S fieldsToReset() {
ret lineCombine(super.fieldsToReset(), [[
ma1 ma2 lastMA1 maMove movingAverage1_cache movingAverage2_cache
maMoveHistory maDirection maDirectionHistory
]]);
}
settable double ma1;
settable double ma2;
settable double lastMA1 = Double.NaN;
settable double maMove;
settable int maDirection;
gettable TickerSequence maMoveHistory = new TickerSequence("maMove");
gettable TickerSequence maDirectionHistory = new TickerSequence("maDirection");
settable transient O visualizer;
selfType emptyClone() {
ret ((selfType) super.emptyClone())
.longSide(longSide.emptyClone())
.shortSide(shortSide.emptyClone());
}
// Side = logic for one side (long/short = direction 1/-1)
record noeq Side(int direction) {
// Our condition is:
// Price has to be in a band close to the MA first (band 1)
// and then move into a band further away from the MA (band 2)
// Are we allowed to open positions in this direction at all?
settable bool enabled = true;
// First band (close to MA)
settable DoubleRange band1 = new(0.2, 0.6);
// Second band
settable DoubleRange band2 = new(0.6, 1.0);
// Percentage to close the position at (below band/above band)
settable double closeLevel = 0;
// Do we require the MA to go in our direction?
// (If no, we just require it to not go the other direction.)
settable bool needMADirection = true;
// temporal fields follow
selfType emptyClone() {
ret resetFields(shallowClone(this), "wasInBand1");
}
// Has price visited band1 recently?
settable bool wasInBand1;
// convert percent band to current price range
DoubleRange instantiateBand(DoubleRange band, double ma1 default ma1) {
ret doubleRange(
maPlusPercent(ma1, direction*band.start),
maPlusPercent(ma1, direction*band.end)).sort();
}
double priceToPercent(double price, double ma1 default ma1) {
ret (price/ma1-1)*direction*100;
}
double relativePrice() {
ret priceToPercent(currentPrice());
}
void step {
var relativePrice = relativePrice();
// close if too close to band/beyond band
if (relativePrice < closeLevel && sign(drift()) == direction) {
closeAllPositions(direction > 0 ? "Below band" : "Above band");
}
// price on wrong side => reset
if (relativePrice < 0) wasInBand1(false);
// Above band2 => reset
if (relativePrice > band2.end) wasInBand1(false);
// We hit band 1
if (band1.contains(relativePrice)) wasInBand1(true);
if (wasInBand1 && band2.contains(relativePrice)
&& ma2*direction < currentPrice()*direction
&& (needMADirection ? maDirection == direction : maDirection != -direction)) {
log("Trigger " + this);
wasInBand1(false);
if (drift() == 0 && !inCooldown())
openPosition(direction);
}
}
// "short" or "long"
toString { ret trading_directionToPositionType(direction); }
LS status() {
ret listCombine(
enabled ? null : "disabled",
"Relative price: "+ formatDouble1(relativePrice()),
!wasInBand1 ? null : "Was in band 1",
"Band 1: " + band1,
"Band 2: " + band2,
"Close level: " + closeLevel
);
}
} // end of Side
void ngsStep aka ngsLogic() {
//double close = currentPrice();
//double drift = drift();
for (side : sides())
side.step();
}
simplyCached MAIndicator movingAverage1() {
ret new MAIndicator(period1);
}
simplyCached MAIndicator movingAverage2() {
ret new MAIndicator(period2);
}
L indicators() {
ret ll(movingAverage1(), movingAverage2());
}
bool maRising() { ret maDirection > 0; }
bool maFalling() { ret maDirection < 0; }
double moveSignal() { ret maMove/maMinMove*100; }
double maPlusPercent(dbl ma1 default ma1, double percent) { ret ma1*(1+percent/100); }
double maMinusPercent(dbl ma1 default ma1, double percent) { ret ma1*(1-percent/100); }
{
granularity(30);
onNewPrice(price -> ngsStep());
onCandleCompleted(candle -> {
lastMA1(ma1);
ma1(movingAverage1()!);
ma2(movingAverage2()!);
maMove((ma1/lastMA1-1)*100);
maMoveHistory?.addIfPriceChanged(maMove, currentTime());
if (maMove >= maMinMove)
maDirection(1);
else if (maMove <= -maMinMove)
maDirection(-1);
else if (maDirection < 0 && maMove >= -maMinMove2()
|| maDirection > 0 && maMove <= maMinMove2())
maDirection(0);
maDirectionHistory?.addIfPriceChanged(maDirection, currentTime());
ngsStep();
});
}
double maMinMove2() { ret maMinMove-maMoveHysteresis; }
LS status() {
ret listCombine(
"Min distance, MA min move: " + maMinMove + " (current move: " + formatDouble(maMove, 4) + ")",
"MA periods: " + period1 + " " + period2,
"Do: " + commaCombine(stringIf(doLongs(), "Longs"), stringIf(doShorts(), "Shorts")),
"Moving averages: " + formatDouble_significant(ma1, 4) + ", " + formatDouble_significant(ma2, 4),
map(sides(), side -> firstToUpper(side) + " side: " + commaCombine(side.status())),
super.status()
);
}
void afterStep :: before {
}
bool doShorts() { ret shortSide.enabled; }
bool doLongs() { ret longSide.enabled; }
L sides() {
ret ll(longSide, shortSide);
}
int candlesNeededBeforeOperational() { ret period1; }
bool usingCells() { false; }
}