Warning: session_start(): open(/var/lib/php/sessions/sess_thlde56qqc3ln3lrug0aakbq7n, 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 CleanMoveStrategy extends G22TradingStrategy {
// How clean the move must be
settable double minCleanness = 2;
// How profitable the move must be
settable double minProfit = 0.5;
// Factor for turning pullback into callback
settable double callbackFactor = 1.5;
// Max pullback of move
settable double maxPullback = 1;
// Price has to move at least this many percent for us to
// recalculate the moves
//settable double minChangeToReactTo = 0.1;
// How often to start a new move analysis (seconds)
settable double calcMovesEvery = 30;
S fieldsToReset() {
ret lines(ll(super.fieldsToReset(), [[currentMoves lastPriceReactedTo movesCalced]]));
}
// current short & long move
settable L currentMoves;
//settable double lastPriceReactedTo;
settable double movesCalced;
persistable class Position extends G22TradingStrategy.Position {
settable CallbackJuicer juicer;
}
srecord ProfitBeforeLeverageJuiceable(Position p) is Juiceable {
public double juiceValue() { ret p.profitBeforeLeverage(); }
}
void price(double price) {
if (currentPrice == price) ret;
currentPrice = price;
afterwards { change(); }
// Close positions
for (p : openPositions()) {
if (p.juicer != null) {
var signals = p.juicer.calculateCloseSignals();
var strongest = highestBy(signals, s -> s.strength());
if (strongest != null && strongest.isTrigger())
p.close(strongest);
}
}
// Check if we should start a position
if (empty(openPositions()) && shouldCalcMoves()) {
// Calculate 2 current moves backwards in time
var time = currentTime();
currentMoves = map(falseTrue(), isShort -> {
new LivePullbackToProfitAnalysis a;
a.pullbackLimit(maxPullback);
a.isShort(isShort);
int idx = actualTicker.indexOfTimestamp(currentTime());
while (idx >= 0 && !a.done())
a.feed(actualTicker.getPricePoint(idx--));
ret a;
});
bool shouldShort = false, shouldLong = false;
PullbackToProfit openReason = null;
for (move : currentMoves)
if (move.cleanness() >= minCleanness && move.profit() >= minProfit) {
openReason = move.ptp();
if (openReason.isShort)
shouldLong = true;
else
shouldShort = true;
}
if (shouldShort && shouldLong)
print("Conflicting signal (long+short at once)");
else if (shouldShort || shouldLong) {
int direction = shouldShort ? -1 : 1;
new Position p;
p.marginToUse = marginPerPosition;
var callbackRate = openReason.pullback*callbackFactor;
p.juicer = new CallbackJuicer(callbackRate);
p.juicer.juiceable(new ProfitBeforeLeverageJuiceable(p));
openPosition(p, direction, openReason);
}
}
}
bool shouldCalcMoves() {
/*if (!differentByEpsilonRatio(lastPriceReactedTo, currentPrice, minChangeToReactTo/100))
false;
lastPriceReactedTo(currentPrice);
true;*/
var time = currentTime();
if (time >= movesCalced+fromSeconds(calcMovesEvery)) {
movesCalced = time;
true;
}
false;
}
L extends Position> openPositions() { ret (L) super.openPositions(); }
LS status() {
ret listCombine(
map(currentMoves, move -> "Move: " + move),
super.status()
);
}
}