Warning: session_start(): open(/var/lib/php/sessions/sess_qq6ghv55956g1d3rp7o2epjuf1, 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
abstract concept G22TradingStrategy extends ConceptWithChangeListeners {
settable bool verbose;
gettable S globalID = aGlobalID();
gettable transient Q q = startQ();
// link to market (optional)
transient settable IFuturesMarket market;
settableWithVar S cryptoCoin;
settableWithVar S marginCoin = "USDT";
settableWithVar bool usingLiveData;
settableWithVar bool doingRealTrades;
settableWithVar bool demoCoin;
settableWithVar bool active;
// moved to archive (out of main list)
settableWithVar bool archived;
settableWithVar double adversity = 0.2;
settableWithVar new LS log;
settableWithVar double epsilon = 1e-6;
// increment of crypto we can bet on
settableWithVar double cryptoStep = 0.0001;
// minimum crypto we need to bet on
settableWithVar double minCrypto = 0.0001;
settableWithVar double maxInvestment;
swappable long currentTime() { ret now(); }
// Note from price feeder
settableWithVar S feedNote;
void log(O o) {
if (o != null) {
log.add(print(str(o)));
change();
}
}
LS activationStatus() {
ret llNempties(
active ? "Active" : "Inactive",
empty(cryptoCoin) ? null
: "coin: " + cryptoCoin + " over " + marginCoin
+ stringIf(demoCoin, " (demo coin)"),
stringIf(market != null, "connected to market"),
stringIf(usingLiveData, "using live data"),
stringIf(doingRealTrades, "real trades")
);
}
abstract Cl openPositions();
abstract Cl closedPositions();
double unrealizedProfit() {
double sum = 0;
for (p : openPositions())
sum += p.profit();
ret sum;
}
double openMargins() {
double sum = 0;
for (p : openPositions())
sum += p.margin();
ret sum;
}
double unrealizedCoinProfit() {
double sum = 0;
for (p : openPositions)
sum += p.coinProfit();
ret sum;
}
L positionsInDirection(int direction) {
ret filter(openPositions, p -> p.direction == direction);
}
L longPositions() { ret positionsInDirection(1); }
L shortPositions() { ret positionsInDirection(-1); }
L shortPositionsAtOrBelowPrice(double openingPrice) {
ret filter(openPositions, p -> p.isShort() && p.openingPrice <= openingPrice);
}
L longPositionsAtOrAbovePrice(double openingPrice) {
ret filter(openPositions, p -> p.isLong() && p.openingPrice >= openingPrice);
}
L negativePositions() {
ret filter(openPositions, p -> p.profit() < 0);
}
double debt() {
ret max(0, -unrealizedCoinProfit());
}
double profit() {
ret realizedProfit+unrealizedProfit();
}
double coinProfit() {
ret realizedCoinProfit+unrealizedCoinProfit();
}
S formatProfit(double x) {
ret plusMinusFix(formatDouble1(x));
}
settableWithVar PriceDigitizer2 digitizer;
settableWithVar int direction;
// maximum debt seen
settableWithVar double maxDebt;
//settableWithVar double minCoinProfit;
//settableWithVar double maxBoundCoin;
class Position {
settable double openingPrice;
settable double direction;
settable double digitizedOpeningPrice;
double closingPrice = Double.NaN;
long openingStep, closingStep;
gettable long openingTime;
gettable long closingTime;
double leverage;
settable double cryptoAmount;
settable O openError;
settable O closeReason;
settable O closeError;
gettable double margin;
settable bool openedOnMarket;
settable bool closedOnMarket;
G22TradingStrategy strategy() { ret G22TradingStrategy.this; }
{
if (!dynamicObjectIsLoading()) {
openingStep = stepCount;
openingTime = currentTime();
}
}
double digitizedOpeningPrice() { ret digitizedOpeningPrice; }
bool isLong() { ret direction > 0; }
bool isShort() { ret direction < 0; }
bool closed() { ret !isNaN(closingPrice); }
S type() { ret trading_directionToPositionType(direction); }
double profitAtPrice(double price) {
ret ((price-openingPrice)/openingPrice*direction*100-adversity)*leverage;
}
double profit() {
ret profitAtPrice(closed() ? closingPrice : currentPrice());
}
double coinProfit() {
ret profit()/100*margin();
}
void close(O closeReason) {
if (closed()) fail("Can't close again");
if (closeReason != null) closeReason(closeReason);
openPositions.remove(this);
closingPrice = currentPrice();
closingTime = currentTime();
closingStep = stepCount;
closedPositions.add(this);
realizedProfit += profit();
realizedCoinProfit += coinProfit();
closeOnMarket();
//print(this);
//printPositions();
}
// Position.toString
toString {
ret commaCombine(
spaceCombine(
closed() ? "Closed " + formatLocalDateWithSeconds(closingTime()) : null,
formatDouble1(leverage) + "X " + upper(type())
),
"opened " + formatLocalDateWithSeconds(openingTime()),
!closed() ? null : or(closeReason, "unknown reason"),
"profit: " + formatProfit(profit()) + "% (" + marginCoin + " " + plusMinusFix(formatPrice(coinProfit())) + ")",
"margin: " + marginCoin + " " + formatPrice(margin()),
"crypto: " + formatPrice(cryptoAmount),
"opening price: " + formatPriceX(openingPrice)
+ " @ step " + openingStep,
!closed() ? null : "closing price: " + formatPriceX(closingPrice)
+ " @ step " + closingStep,
//"digitized: " + formatPrice(digitizedOpeningPrice()),
);
}
void closeOnMarket {
try {
if (market != null) {
log("Closing on market: " + this);
market.closePosition(new IFuturesMarket.CloseOrder()
.holdSide(HoldSide.fromInt(direction))
.cryptoAmount(cryptoAmount));
closedOnMarket(true);
change();
}
} catch e {
closeError = toPersistableThrowable(e);
}
}
void open {
margin = cryptoAmount*openingPrice/leverage;
openPositions.add(this);
}
void openOnMarket {
try {
if (market != null) {
log("Opening on market: " + this);
market.openPosition(new IFuturesMarket.OpenOrder()
.holdSide(HoldSide.fromInt(direction))
.cryptoAmount(cryptoAmount)
.leverage(leverage)
.isCross(true));
openedOnMarket(true);
change();
}
} catch e {
openError(toPersistableThrowable(e));
log("Open error:" + e);
close("Open error");
}
}
}
gettable double currentPrice = 0;
gettable double oldPrice = Double.NaN;
gettable double startingPrice = Double.NaN;
gettable int lastDirection;
gettable double realizedProfit;
gettable double realizedCoinProfit;
settableWithVar long stepCount;
settableWithVar long stepSince;
gettable new LinkedHashSet openPositions;
gettable new L closedPositions;
bool hasPosition(double price, double direction) {
ret findPosition(price, direction) != null;
}
Position closePosition(double price, double direction, O closeReason) {
var p = findPosition(price, direction);
p?.close(closeReason);
ret p;
}
void closePositions(Cl positions, O closeReason default null) {
forEach(positions, -> .close(closeReason));
}
Position findPosition(double digitizedPrice, double direction) {
ret firstThat(openPositions(), p ->
diffRatio(p.digitizedOpeningPrice(), digitizedPrice) <= epsilon() && p.direction == direction);
}
Position openShort() { ret openPosition(-1); }
Position openLong() { ret openPosition(1); }
void printPositions {
print(colonCombine(n2(openPositions, "open position"),
joinWithComma(openPositions)));
}
bool started() { ret !isNaN(startingPrice); }
void prices(double... prices) {
fOr (price : prices)
price(price);
}
swappable PriceCells makePriceCells(double basePrice) {
ret new GeometricPriceCells(basePrice, cellSize);
}
double digitizedPrice() { ret digitizer.digitizedPrice(); }
double lastDigitizedPrice() { ret digitizer.lastDigitizedPrice(); }
void handleNewPriceInQ(double price) {
q.add(-> price(price));
}
void nextStep {
++stepCount;
stepSince(now());
}
void afterStep {
maxDebt = max(maxDebt, debt());
//minCoinProfit = min(minCoinProfit, coinProfit());
//maxBoundCoin = max(maxBoundCoin, boundCoin());
maxInvestment = max(maxInvestment, investment());
change();
}
double investment() {
ret boundCoin()-realizedCoinProfit;
}
double boundCoin() {
ret max(openMargins(), max(0, -coinProfit()));
}
double fromCellNumber(double cellNumber) { ret cells().fromCellNumber(cellNumber); }
double toCellNumber(double price) { ret cells().toCellNumber(price); }
L shortPositionsAtOrBelowDigitizedPrice(double openingPrice) {
ret filter(openPositions, p -> p.isShort() && p.digitizedOpeningPrice() <= openingPrice);
}
L shortPositionsAtOrAboveDigitizedPrice(double openingPrice) {
ret filter(openPositions, p -> p.isShort() && p.digitizedOpeningPrice() >= openingPrice);
}
L longPositionsAtOrAboveDigitizedPrice(double openingPrice) {
ret filter(openPositions, p -> p.isLong() && p.digitizedOpeningPrice() >= openingPrice);
}
L longPositionsAtOrBelowDigitizedPrice(double openingPrice) {
ret filter(openPositions, p -> p.isLong() && p.digitizedOpeningPrice() <= openingPrice);
}
PriceCells cells() {
ret digitizer?.cells;
}
S formatPriceX(double price) {
if (isNaN(price)) ret "-";
double num = cells().priceToCellNumber(price);
ret formatPrice(price) + " (C" + formatDouble2(num) + ")";
}
}