longPositionsAtOrBelowDigitizedPrice(double openingPrice) {
return filter(openPositions, p -> p.isLong() && p.digitizedOpeningPrice() <= openingPrice);
}
final PriceCells priceCells(){ return cells(); }
PriceCells cells() {
return digitizer == null ? null : digitizer.cells;
}
String formatPriceX(double price) {
if (isNaN(price)) return "-";
String s = formatPrice(price);
if (cells() == null) return s;
double num = cells().priceToCellNumber(price);
return s + " (C" + formatDouble2(num) + ")";
}
final void currentPrice(double price){ price(price); }
abstract void price(double price);
P openPosition(P p, int direction) { return openPosition(p, direction, null); }
P openPosition(P p, int direction, Object openReason) {
p.openReason(openReason);
var price = digitizedPrice();
var realPrice = currentPrice();
logVars("openPosition", "realPrice", realPrice, "price", price, "digitizer", digitizer);
if ((isNaN(price) || price == 0) && digitizer != null) {
price = digitizer.digitizeIndividually(currentPrice());
print("digitized individually: " + price);
}
p.openingPrice(realPrice);
p.direction(direction);
p.digitizedOpeningPrice(price);
double cryptoAmount = p.marginToUse/realPrice*leverage;
cryptoAmount = roundTo(cryptoStep, cryptoAmount);
log(renderVars("openPosition", "marginPerPosition", marginPerPosition, "realPrice", realPrice, "leverage", leverage, "cryptoAmount", cryptoAmount, "cryptoStep", cryptoStep));
p.cryptoAmount = max(minCrypto, cryptoAmount);
p.open();
//print("Opening " + p);
//printPositions();
p.openOnMarket();
return p;
}
List status() {
double mulProf = multiplicativeProfit();
return llNonNulls(
empty(comment) ? null : "Comment: " + comment,
"Profit: " + marginCoin + " " + plusMinusFix(formatMarginPrice(coinProfit())),
"Realized profit: " + marginCoin + " " + formatMarginProfit(realizedCoinProfit) + " from " + n2(closedPositions, "closed position")
+ " (" + formatMarginProfit(realizedCoinWins) + " from " + n2(realizedWins, "win")
+ ", " + formatMarginProfit(realizedCoinLosses) + " from " + n2(realizedLosses, "loss", "losses") + ")",
"Unrealized profit: " + marginCoin + " " + formatMarginProfit(unrealizedCoinProfit()) + " in " + n2(openPositions, "open position"),
isNaN(mulProf) ? null : "Multiplicative profit: " + formatProfit(mulProf) + "%",
//baseToString(),
!primed() ? null : "Primed",
!started() ? null : "Started. current price: " + formatPriceX(currentPrice)
+ (isNaN(digitizedPrice()) ? "" : ", digitized: " + formatPriceX(digitizedPrice())),
"Leverage: " + leverage + ", margin per position: " + marginCoin + " " + formatPrice(marginPerPosition),
"Cell size: " + formatCellSize(cellSize),
spaceCombine("Step " + n2(stepCount), renderStepSince()),
//"Debt: " + marginCoin + " " + formatMarginProfit(debt()) + " (max seen: " + formatMarginProfit(maxDebt) + ")",
"Investment used: " + marginCoin + " " + formatMarginPrice(maxInvestment()),
strategyJuicer == null ? null : "Strategy juicer: " + strategyJuicer,
"Drift: " + cryptoCoin + " " + plusMinusFix(formatCryptoAmount(drift())));
}
String renderStepSince() {
if (stepSince == 0) return "";
return "since " + (active()
? formatHoursMinutesColonSeconds(currentTime()-stepSince)
: formatLocalDateWithSeconds(stepSince));
}
List fullStatus() {
return listCombine(
status(),
"",
n2(openPositions, "open position") + ":",
reversed(openPositions),
"",
n2(closedPositions, "closed position")
+ " (" + (showClosedPositionsBackwards ? "latest first" : "oldest first") + "):",
showClosedPositionsBackwards ? reversed(closedPositions) : closedPositions);
}
void feed(PricePoint pricePoint) {
if (!active()) return;
setTime(pricePoint.timestamp);
price(pricePoint.price);
}
void feed(TickerSequence ts) {
if (!active()) return;
if (ts == null) return;
for (var pricePoint : ts.pricePoints())
feed(pricePoint);
}
public int compareTo(G22TradingStrategy s) {
return s == null ? 1 : cmp(coinProfit(), s.coinProfit());
}
void closeAllPositions() { closeAllPositions("User close"); }
void closeAllPositions(Object reason) {
closePositions(openPositions(), reason);
}
void closeMyself() {
closedItself(currentTime());
closeAllPositionsAndDeactivate();
}
void closeAllPositionsAndDeactivate() {
deactivate();
closeAllPositions();
}
void deactivate() {
if (!active) return;
active(false);
deactivated(currentTime());
log("Strategy deactivated.");
}
final void reset_G22TradingStrategy(){ reset(); }
void reset() {
resetFields(this, fieldsToReset());
change();
}
final G22TradingStrategy emptyClone_G22TradingStrategy(){ return emptyClone(); }
G22TradingStrategy emptyClone() {
var clone = shallowCloneToUnlistedConcept(this);
clone.reset();
return clone;
}
List allPositions() {
return concatLists(openPositions, closedPositions);
}
List sortedPositions() {
var allPositions = allPositions();
return sortedByCalculatedField(allPositions, __6 -> __6.openingTime());
}
boolean positionsAreNonOverlapping() {
for (var __0: overlappingPairs(sortedPositions()))
{ var a = pairA(__0); var b = pairB(__0); if (b.openingTime() < a.closingTime())
return false; }
return true;
}
// Profit when applying all positions (somewhat theoretical because you
// might go below platform limits)
// Also only works when positions are linear
double multiplicativeProfit() {
if (!positionsAreNonOverlapping()) return Double.NaN;
double profit = 1;
for (var p : sortedPositions())
profit *= 1+p.profit()/100;
return (profit-1)*100;
}
boolean haveBackData() { return backDataHoursWanted == 0 | backDataFed; }
boolean didRealTrades() {
return any(allPositions(), p -> p.openedOnMarket() || p.closedOnMarket());
}
String formatCellSize(double cellSize) {
return formatPercentage(cellSize, 3);
}
String areaDesc() {
if (eq(area, "Candidates")) return "Candidate";
return nempty(area) ? area : archived ? "Archived" : "";
}
final G22TradingStrategy currentTime(long time){ return setTime(time); }
G22TradingStrategy setTime(long time) {
int age = ifloor(ageInHours());
long lastMod = mod(currentTime()-startTime, hoursToMS(1));
currentTime = () -> time;
if (ifloor(ageInHours()) > age)
log("Hourly profit log: " + formatMarginProfit(coinProfit()));
return this;
}
double ageInHours() { return startTime == 0 ? 0 : msToHours(currentTime()-startTime); }
// only use near start of strategy, untested otherwise
G22TradingStrategy changeCellSize(double newCellSize) {
double oldCellSize = cellSize();
if (oldCellSize == newCellSize) return this;
cellSize(newCellSize);
if (digitizer != null)
digitizer.swapPriceCells(makePriceCells(priceCells().basePrice()));
log("Changed cell size from " + oldCellSize + " to " + newCellSize);
return this;
}
boolean hasClosedItself() { return closedItself != 0; }
public double juiceValue() { return coinProfit(); }
double drift() {
double drift = 0;
for (var p : openPositions())
drift += p.cryptoAmount()*p.direction();
return drift;
}
String formatCryptoAmount(double amount) {
return formatDouble3(amount);
}
Position openShort() { return openPosition(-1); }
Position openLong() { return openPosition(1); }
Position openPosition(int direction) { return openPosition(direction, null); }
Position openPosition(int direction, Object openReason) {
Position p = new Position();
p.marginToUse = marginPerPosition;
return openPosition(p, direction, openReason);
}
List winners() { return filter(closedPositions(), p -> p.coinProfit() > 0); }
List losers() { return filter(closedPositions(), p -> p.coinProfit() < 0); }
List extends Position> openPositions() { return cloneList(openPositions); }
}
interface IFuturesMarket {
// all open/close orders are market right now (not limit)
static class OpenOrder {
final public OpenOrder setHoldSide(HoldSide holdSide){ return holdSide(holdSide); }
public OpenOrder holdSide(HoldSide holdSide) { this.holdSide = holdSide; return this; } final public HoldSide getHoldSide(){ return holdSide(); }
public HoldSide holdSide() { return holdSide; }
HoldSide holdSide;
final public OpenOrder setCryptoAmount(double cryptoAmount){ return cryptoAmount(cryptoAmount); }
public OpenOrder cryptoAmount(double cryptoAmount) { this.cryptoAmount = cryptoAmount; return this; } final public double getCryptoAmount(){ return cryptoAmount(); }
public double cryptoAmount() { return cryptoAmount; }
double cryptoAmount;
final public OpenOrder setLeverage(double leverage){ return leverage(leverage); }
public OpenOrder leverage(double leverage) { this.leverage = leverage; return this; } final public double getLeverage(){ return leverage(); }
public double leverage() { return leverage; }
double leverage = 1;
final public OpenOrder setIsCross(boolean isCross){ return isCross(isCross); }
public OpenOrder isCross(boolean isCross) { this.isCross = isCross; return this; } final public boolean getIsCross(){ return isCross(); }
public boolean isCross() { return isCross; }
boolean isCross = false;
}
static class CloseOrder {
final public CloseOrder setHoldSide(HoldSide holdSide){ return holdSide(holdSide); }
public CloseOrder holdSide(HoldSide holdSide) { this.holdSide = holdSide; return this; } final public HoldSide getHoldSide(){ return holdSide(); }
public HoldSide holdSide() { return holdSide; }
HoldSide holdSide;
final public CloseOrder setCryptoAmount(double cryptoAmount){ return cryptoAmount(cryptoAmount); }
public CloseOrder cryptoAmount(double cryptoAmount) { this.cryptoAmount = cryptoAmount; return this; } final public double getCryptoAmount(){ return cryptoAmount(); }
public double cryptoAmount() { return cryptoAmount; }
double cryptoAmount;
}
// throws exception if order failed
void openPosition(OpenOrder order);
// throws exception if order failed
void closePosition(CloseOrder order);
static class SwappableImplementation implements IFuturesMarket {
public transient IVF1 openPosition;
public void openPosition(OpenOrder order) { if (openPosition != null) openPosition.get(order); else openPosition_base(order); }
final public void openPosition_fallback(IVF1 _f, OpenOrder order) { if (_f != null) _f.get(order); else openPosition_base(order); }
public void openPosition_base(OpenOrder order) {}
public transient IVF1 closePosition;
public void closePosition(CloseOrder order) { if (closePosition != null) closePosition.get(order); else closePosition_base(order); }
final public void closePosition_fallback(IVF1 _f, CloseOrder order) { if (_f != null) _f.get(order); else closePosition_base(order); }
public void closePosition_base(CloseOrder order) {}
public transient IF0 drift;
public double drift() { return drift != null ? drift.get() : drift_base(); }
final public double drift_fallback(IF0 _f) { return _f != null ? _f.get() : drift_base(); }
public double drift_base() { throw unimplemented(); }
public FutureCoinParameters getCoinParameters() { throw unimplemented(); }
}
// Get current drift (sum of longs+shorts in crypto units)
double drift();
FutureCoinParameters getCoinParameters();
}
static class SynchronizedLongBuffer implements ILongBuffer {
long[] data;
int size;
SynchronizedLongBuffer() {}
SynchronizedLongBuffer(int size) { if (size != 0) data = new long[size]; }
SynchronizedLongBuffer(Iterable l) {
if (l instanceof Collection) allocate(((Collection) l).size());
addAll(l);
}
public synchronized void add(long i) {
if (size >= lLongArray(data)) {
data = resizeLongArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lLongArray(data)*2L))));
if (size >= data.length) throw fail("LongBuffer too large: " + size);
}
data[size++] = i;
}
synchronized void allocate(int n) {
data = resizeLongArray(data, max(n, size()));
}
public synchronized void addAll(Iterable l) {
if (l != null) for (long i : l) add(i);
}
public synchronized long[] toArray() {
return size == 0 ? null : resizeLongArray(data, size);
}
synchronized List toList() {
return longArrayToList(data, 0, size);
}
public synchronized List asVirtualList() {
return listFromFunction(__47 -> get(__47), size);
}
synchronized void reset() { size = 0; }
void clear() { reset(); }
synchronized public int size() { return size; }
public synchronized boolean isEmpty() { return size == 0; }
public synchronized long get(int idx) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
return data[idx];
}
synchronized void set(int idx, long value) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
data[idx] = value;
}
public synchronized long popLast() {
if (size == 0) throw fail("empty buffer");
return data[--size];
}
public synchronized long last() { return data[size-1]; }
synchronized long nextToLast() { return data[size-2]; }
public String toString() { return squareBracket(joinWithSpace(toList())); }
public synchronized Iterator iterator() {
return new IterableIterator() {
int i = 0;
public boolean hasNext() { return i < size(); }
public Long next() {
synchronized(SynchronizedLongBuffer.this) {
if (!hasNext()) throw fail("Index out of bounds: " + i);
return data[i++];
}
}
};
}
public synchronized void trimToSize() {
data = resizeLongArray(data, size);
}
synchronized void remove(int idx) {
arraycopy(data, idx+1, data, idx, size-1-idx);
--size;
}
// don't rely on return value if buffer is empty
public synchronized long poll() {
return size == 0 ? -1 : data[--size];
}
public synchronized void insertAt(int idx, long[] l) {
int n = l(l);
if (n == 0) return;
long[] newData = new long[size+n];
arraycopy(data, 0, newData, 0, idx);
arraycopy(l, 0, newData, idx, n);
arraycopy(data, idx, newData, idx+n, size-idx);
data = newData;
size = newData.length;
}
}
static class SynchronizedFloatBufferPresentingAsDoubles implements IDoubleBuffer {
float[] data;
int size;
SynchronizedFloatBufferPresentingAsDoubles() {}
SynchronizedFloatBufferPresentingAsDoubles(int size) { if (size != 0) data = new float[size]; }
SynchronizedFloatBufferPresentingAsDoubles(Iterable l) { addAll(l); }
SynchronizedFloatBufferPresentingAsDoubles(Collection l) { this(l(l)); addAll(l); }
SynchronizedFloatBufferPresentingAsDoubles(double... data) { this.data = doubleArrayToFloatArray(data); size = l(data); }
public synchronized void add(double i) {
if (size >= lFloatArray(data)) {
data = resizeFloatArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lFloatArray(data)*2L))));
if (size >= data.length) throw fail(shortClassName(this) + " too large: " + size);
}
data[size++] = (float) i;
}
public synchronized void addAll(Iterable l) {
if (l != null) for (double i : l) add(i);
}
public synchronized double[] toArray() {
return size == 0 ? null : takeFirstFromFloatArrayAsDoubleArray(data, size);
}
double[] toArrayNonNull() {
return unnull(toArray());
}
synchronized List toList() {
return floatArrayToDoubleList(data, 0, size);
}
synchronized List asVirtualList() {
return new RandomAccessAbstractList() {
public int size() { return SynchronizedFloatBufferPresentingAsDoubles.this.size(); }
public Double get(int i) { return SynchronizedFloatBufferPresentingAsDoubles.this.get(i); }
public Double set(int i, Double val) {
synchronized(SynchronizedFloatBufferPresentingAsDoubles.this) {
Double a = get(i);
data[i] = val.floatValue();
return a;
}
}
};
}
synchronized void reset() { size = 0; }
void clear() { reset(); }
public synchronized int size() { return size; }
public synchronized boolean isEmpty() { return size == 0; }
public synchronized double get(int idx) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
return data[idx];
}
synchronized void set(int idx, double value) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
data[idx] = (float) value;
}
public synchronized double popLast() {
if (size == 0) throw fail("empty buffer");
return data[--size];
}
public synchronized double first() { return data[0]; }
public synchronized double last() { return data[size-1]; }
synchronized double nextToLast() { return data[size-2]; }
public String toString() { return squareBracket(joinWithSpace(toList())); }
public Iterator iterator() {
return new IterableIterator() {
int i = 0;
public boolean hasNext() { return i < size(); }
public Double next() {
synchronized(SynchronizedFloatBufferPresentingAsDoubles.this) {
//if (!hasNext()) fail("Index out of bounds: " + i);
return (double) data[i++];
}
}
};
}
/*public DoubleIterator doubleIterator() {
ret new DoubleIterator {
int i = 0;
public bool hasNext() { ret i < size; }
public int next() {
//if (!hasNext()) fail("Index out of bounds: " + i);
ret data[i++];
}
toString { ret "Iterator@" + i + " over " + DoubleBuffer.this; }
};
}*/
public synchronized void trimToSize() {
data = resizeFloatArray(data, size);
}
synchronized int indexOf(double b) {
for (int i = 0; i < size; i++)
if (data[i] == b)
return i;
return -1;
}
synchronized double[] subArray(int start, int end) {
return subFloatArrayAsDoubleArray(data, start, min(end, size));
}
public synchronized void insertAt(int idx, double[] l) {
int n = l(l);
if (n == 0) return;
float[] newData = new float[size+n];
arraycopy(data, 0, newData, 0, idx);
for (int i = 0; i < n; i++)
newData[idx+i] = (float) l[i];
arraycopy(data, idx, newData, idx+n, size-idx);
data = newData;
size = newData.length;
}
}
enum HoldSide {
SHORT, LONG;
static HoldSide fromInt(double direction) {
if (direction > 0) return LONG;
if (direction < 0) return SHORT;
throw fail("direction 0");
}
boolean isLong() { return this == LONG; }
boolean isShort() { return this == SHORT; }
}
static class Q implements AutoCloseable {
String name = "Unnamed Queue";
List q = synchroLinkedList();
ReliableSingleThread rst = new ReliableSingleThread(new Runnable() { public void run() { try { _run() ;
} catch (Exception __e) { throw rethrow(__e); } } public String toString() { return "_run()"; }});
final public boolean getRetired(){ return retired(); }
public boolean retired() { return retired; }
volatile boolean retired = false;
final public Runnable getCurrentJob(){ return currentJob(); }
public Runnable currentJob() { return currentJob; }
volatile Runnable currentJob;
AtomicLong jobsDone = new AtomicLong();
Q() {}
Q(String name) {
this.name = name;}
void add(Runnable r) {
assertNotRetired();
q.add(r);
_trigger();
}
void addInFront(Runnable r) {
assertNotRetired();
q.add(0, r);
_trigger();
}
void _trigger() {
rst.name = name;
rst.go();
}
void add(Object r) {
add(toRunnable(r));
}
void _run() {
Runnable r;
while (licensed() && !retired && (r = syncPopFirst(q)) != null) {
currentJob = r;
inc(jobsDone);
try { r.run(); } catch (Throwable __e) { pcallFail(__e); }
currentJob = null;
}
onIdle();
}
public void close() { retired = true; } // TODO: interrupt thread
void done() {} // legacy function
boolean isEmpty() { return q.isEmpty(); }
int size() { return q.size(); }
Object mutex() { return q; } // clients can synchronize on this
List snapshot() { return cloneList(q); }
// override this
void onIdle() {}
boolean busy() { return currentJob != null; }
void assertNotRetired() {
assertFalse("Queue is retired", retired());
}
boolean hasThread() { return rst.hasThread(); }
long nJobsDone() { return jobsDone.get(); }
public String toString() {
return (retired ? "Retired " : "") + "Q " + systemHashCodeHex(this)
+ " (" + (isEmpty() ? "empty" : nEntries(size()) + ", current job: " + currentJob) + ")";
}
}
interface ILongBuffer extends IntSize, Iterable, ILongQueue {
void add(long d);
void addAll(Iterable l);
long[] toArray();
long get(int idx);
void trimToSize();
long popLast();
long last();
boolean isEmpty();
List asVirtualList();
void insertAt(int idx, long[] l);
}
static class ConceptWithChangeListeners extends Concept implements IHasChangeListeners, ChangeTriggerable {
transient Set onChange;
public ConceptWithChangeListeners onChange(Runnable r) { onChange = createOrAddToSyncLinkedHashSet(onChange, r); return this; }
public ConceptWithChangeListeners removeChangeListener(Runnable r) { main.remove(onChange, r); return this; }
public void fireChange() { if (onChange != null) for (var listener : onChange) pcallF_typed(listener); }
void _onChange() { super._onChange();
fireChange();
}
public void change() { super.change(); }
}
interface IDoubleBuffer extends IntSize, Iterable {
void add(double d);
void addAll(Iterable l);
double[] toArray();
double get(int idx);
void trimToSize();
double popLast();
double first();
double last();
boolean isEmpty();
void insertAt(int idx, double[] l);
}
interface IntSize {
int size();
default boolean isEmpty() { return size() == 0; }
}
interface Juiceable {
// e.g. a position's profit before leverage
double juiceValue();
}
static class FieldVar extends VarWithNotify {
IHasChangeListeners containingObject;
String fieldName;
IF0 getter;
IVF1 setter;
FieldVar(IHasChangeListeners containingObject,
String fieldName, IF0 getter, IVF1 setter) {
this.setter = setter;
this.getter = getter;
this.fieldName = fieldName;
this.containingObject = containingObject;
containingObject.onChangeAndNow(() -> _updateFromObject());
}
void _updateFromObject() {
set(getter.get());
}
public void fireChange() {
setter.get(get());
super.fireChange(); }
public FieldVar onChange(IVF1 r) {
if (r != null) onChange(() -> r.get(get()));
return this;
}
}
static class GeometricPriceCells implements PriceCells {
GeometricPriceCells() {}
// cell size in percent
final public GeometricPriceCells setCellSizeInPercent(double cellSizeInPercent){ return cellSizeInPercent(cellSizeInPercent); }
public GeometricPriceCells cellSizeInPercent(double cellSizeInPercent) { this.cellSizeInPercent = cellSizeInPercent; return this; } final public double getCellSizeInPercent(){ return cellSizeInPercent(); }
public double cellSizeInPercent() { return cellSizeInPercent; }
double cellSizeInPercent;
// one of the cell limits
final public GeometricPriceCells setBasePrice(double basePrice){ return basePrice(basePrice); }
public GeometricPriceCells basePrice(double basePrice) { this.basePrice = basePrice; return this; } final public double getBasePrice(){ return basePrice(); }
public double basePrice() { return basePrice; }
double basePrice = 1000;
GeometricPriceCells(double cellSizeInPercent) {
this.cellSizeInPercent = cellSizeInPercent;}
GeometricPriceCells(double basePrice, double cellSizeInPercent) {
this.cellSizeInPercent = cellSizeInPercent;
this.basePrice = basePrice;}
double ratio() {
return 1+cellSizeInPercent/100;
}
double toLogScale(double price) {
return log(price, ratio());
}
double fromLogScale(double logPrice) {
return pow(ratio(), logPrice);
}
double logBasePrice() {
return toLogScale(basePrice);
}
double remainder(double price) {
return remainder(toLogScale(price));
}
double remainderLog(double logPrice) {
return frac(logPrice-logBasePrice());
}
// TODO: fix this logic's rounding problems
public boolean isCellLimit(double price) {
return remainder(price) == 0;
}
public double nextCellLimitLog(double logPrice) {
double r = remainderLog(logPrice);
return logPrice + 1-r;
}
public double nextCellLimit(double price) {
return fromLogScale(nextCellLimitLog(toLogScale(price)));
}
public double previousCellLimitLog(double logPrice) {
double r = remainderLog(logPrice);
return logPrice - (r == 0 ? 1 : r);
}
public double previousCellLimit(double price) {
return fromLogScale(previousCellLimitLog(toLogScale(price)));
}
public double nCellLimitsDown(double price, int n) {
double logPrice = toLogScale(price);
logPrice = previousCellLimitLog(logPrice)-(n-1);
return fromLogScale(logPrice);
}
public double nCellLimitsUp(double price, int n) {
double logPrice = toLogScale(price);
logPrice = nextCellLimitLog(logPrice)+n;
return fromLogScale(logPrice);
}
public double priceToCellNumber(double price) {
return toLogScale(price)-logBasePrice();
}
public double cellNumberToPrice(double cellNumber) {
return fromLogScale(cellNumber+logBasePrice());
}
public String toString() {
return formatDouble2(cellSizeInPercent) + "% cells with C0=" + formatPrice(basePrice);
}
}
static class PriceDigitizer2 implements IFieldsToList{
PriceCells cells;
PriceDigitizer2() {}
PriceDigitizer2(PriceCells cells) {
this.cells = cells;}
public String toString() { return shortClassName_dropNumberPrefix(this) + "(" + cells + ")"; }public Object[] _fieldsToList() { return new Object[] {cells}; }
final public PriceDigitizer2 setCellNumber(int cellNumber){ return cellNumber(cellNumber); }
public PriceDigitizer2 cellNumber(int cellNumber) { this.cellNumber = cellNumber; return this; } final public int getCellNumber(){ return cellNumber(); }
public int cellNumber() { return cellNumber; }
int cellNumber = Integer.MIN_VALUE;
final public PriceDigitizer2 setLastCellNumber(int lastCellNumber){ return lastCellNumber(lastCellNumber); }
public PriceDigitizer2 lastCellNumber(int lastCellNumber) { this.lastCellNumber = lastCellNumber; return this; } final public int getLastCellNumber(){ return lastCellNumber(); }
public int lastCellNumber() { return lastCellNumber; }
int lastCellNumber = Integer.MIN_VALUE;
final public PriceDigitizer2 setVerbose(boolean verbose){ return verbose(verbose); }
public PriceDigitizer2 verbose(boolean verbose) { this.verbose = verbose; return this; } final public boolean getVerbose(){ return verbose(); }
public boolean verbose() { return verbose; }
boolean verbose = false;
final public PriceDigitizer2 setUpDownSequence(UpDownSequence upDownSequence){ return upDownSequence(upDownSequence); }
public PriceDigitizer2 upDownSequence(UpDownSequence upDownSequence) { this.upDownSequence = upDownSequence; return this; } final public UpDownSequence getUpDownSequence(){ return upDownSequence(); }
public UpDownSequence upDownSequence() { return upDownSequence; }
UpDownSequence upDownSequence = new UpDownSequence();
// returns new digitized price
double digitize(double price) {
double cn = cells.toCellNumber(price);
if (cellNumber == Integer.MIN_VALUE) {
cellNumber = lastCellNumber = iround(cn);
} else {
lastCellNumber = cellNumber;
cellNumber = iroundTowardsWithOutwardEpsilon(cn, cellNumber, epsilon());
// TODO: assuming there are only 1-steps
if (cellNumber > lastCellNumber)
{ if (upDownSequence != null) upDownSequence.addUp(); }
else if (cellNumber < lastCellNumber)
{ if (upDownSequence != null) upDownSequence.addDown(); }
}
return digitizedPrice();
}
double epsilon() { return 1e-4; }
double digitizedPrice() { return cells.fromCellNumber(cellNumber); }
double lastDigitizedPrice() { return cells.fromCellNumber(lastCellNumber); }
// digitize price without looking at history
double digitizeIndividually(double price) {
double cn = cells.toCellNumber(price);
return cells.fromCellNumber(round(cn));
}
PriceCells priceCells() { return cells; }
// brave
void swapPriceCells(PriceCells newPriceCells) {
cells = newPriceCells;
}
}
static class DoubleBuffer implements Iterable, IntSize {
double[] data;
int size;
DoubleBuffer() {}
DoubleBuffer(int size) { if (size != 0) data = new double[size]; }
DoubleBuffer(Iterable l) { addAll(l); }
DoubleBuffer(Collection l) { this(l(l)); addAll(l); }
DoubleBuffer(double... data) { this.data = data; size = l(data); }
void add(double i) {
if (size >= lDoubleArray(data)) {
data = resizeDoubleArray(data, Math.max(1, toInt(Math.min(maximumSafeArraySize(), lDoubleArray(data)*2L))));
if (size >= data.length) throw fail("DoubleBuffer too large: " + size);
}
data[size++] = i;
}
void addAll(Iterable l) {
if (l != null) for (double i : l) add(i);
}
double[] toArray() {
return size == 0 ? null : resizeDoubleArray(data, size);
}
double[] toArrayNonNull() {
return unnull(toArray());
}
List toList() {
return doubleArrayToList(data, 0, size);
}
List asVirtualList() {
return new RandomAccessAbstractList() {
public int size() { return size; }
public Double get(int i) { return DoubleBuffer.this.get(i); }
public Double set(int i, Double val) {
Double a = get(i);
data[i] = val;
return a;
}
};
}
void reset() { size = 0; }
void clear() { reset(); }
public int size() { return size; }
double get(int idx) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
return data[idx];
}
void set(int idx, double value) {
if (idx >= size) throw fail("Index out of range: " + idx + "/" + size);
data[idx] = value;
}
double popLast() {
if (size == 0) throw fail("empty buffer");
return data[--size];
}
double last() { return data[size-1]; }
double nextToLast() { return data[size-2]; }
public String toString() { return squareBracket(joinWithSpace(toList())); }
public Iterator iterator() {
return new IterableIterator() {
int i = 0;
public boolean hasNext() { return i < size; }
public Double next() {
//if (!hasNext()) fail("Index out of bounds: " + i);
return data[i++];
}
};
}
/*public DoubleIterator doubleIterator() {
ret new DoubleIterator {
int i = 0;
public bool hasNext() { ret i < size; }
public int next() {
//if (!hasNext()) fail("Index out of bounds: " + i);
ret data[i++];
}
toString { ret "Iterator@" + i + " over " + DoubleBuffer.this; }
};
}*/
void trimToSize() {
data = resizeDoubleArray(data, size);
}
int indexOf(double b) {
for (int i = 0; i < size; i++)
if (data[i] == b)
return i;
return -1;
}
double[] subArray(int start, int end) {
return subDoubleArray(data, start, min(end, size));
}
}
static class PricePoint implements IFieldsToList{
static final String _fieldOrder = "timestamp price";
long timestamp;
double price;
PricePoint() {}
PricePoint(long timestamp, double price) {
this.price = price;
this.timestamp = timestamp;}
public boolean equals(Object o) {
if (!(o instanceof PricePoint)) return false;
PricePoint __1 = (PricePoint) o;
return timestamp == __1.timestamp && price == __1.price;
}
public int hashCode() {
int h = 516290023;
h = boostHashCombine(h, _hashCode(timestamp));
h = boostHashCombine(h, _hashCode(price));
return h;
}
public Object[] _fieldsToList() { return new Object[] {timestamp, price}; }
public String toString() {
return formatPrice(price) + " @ " + formatLocalDateWithSeconds(timestamp);
}
final long timestamp(){ return time(); }
long time() { return timestamp; }
double price() { return price; }
}
interface PriceCells {
boolean isCellLimit(double price);
// next-higher cell limit or NaN if no more cells
double nextCellLimit(double price);
// next-lower cell limit or NaN if no more cells
double previousCellLimit(double price);
// go n steps down
double nCellLimitsDown(double price, int n);
// go n steps up
double nCellLimitsUp(double price, int n);
// convert to "cell space" (0=first cell limit, 1=next cell limit, -1=previous cell limit etc)
default double toCellNumber(double price){ return priceToCellNumber(price); }
double priceToCellNumber(double price);
default double fromCellNumber(double cellNumber){ return cellNumberToPrice(cellNumber); }
double cellNumberToPrice(double cellNumber);
default double basePrice() { return cellNumberToPrice(0); }
}
// We use big-endian as DataOutputStream does
static class ByteHead /*is DataOutput*/ implements AutoCloseable {
final public ByteHead setReadMode(boolean readMode){ return readMode(readMode); }
public ByteHead readMode(boolean readMode) { this.readMode = readMode; return this; } final public boolean getReadMode(){ return readMode(); }
public boolean readMode() { return readMode; }
boolean readMode = false;
final public ByteHead setWriteMode(boolean writeMode){ return writeMode(writeMode); }
public ByteHead writeMode(boolean writeMode) { this.writeMode = writeMode; return this; } final public boolean getWriteMode(){ return writeMode(); }
public boolean writeMode() { return writeMode; }
boolean writeMode = false;
final public InputStream getInputStream(){ return inputStream(); }
public InputStream inputStream() { return inputStream; }
InputStream inputStream;
final public OutputStream getOutputStream(){ return outputStream(); }
public OutputStream outputStream() { return outputStream; }
OutputStream outputStream;
final public ByteHead setByteCounter(long byteCounter){ return byteCounter(byteCounter); }
public ByteHead byteCounter(long byteCounter) { this.byteCounter = byteCounter; return this; } final public long getByteCounter(){ return byteCounter(); }
public long byteCounter() { return byteCounter; }
long byteCounter;
final public boolean getEof(){ return eof(); }
public boolean eof() { return eof; }
boolean eof = false;
ByteHead() {}
ByteHead(InputStream inputStream) { inputStream(inputStream); }
ByteHead(OutputStream outputStream) { outputStream(outputStream); }
ByteHead inputStream(InputStream inputStream) { this.inputStream = inputStream; readMode(true); return this; }
ByteHead outputStream(OutputStream outputStream) { this.outputStream = outputStream; writeMode(true); return this; }
void write(byte[] data) { try {
ensureWriteMode();
{ if (outputStream != null) outputStream.write(data); }
byteCounter += data.length;
} catch (Exception __e) { throw rethrow(__e); } }
void writeFloat(float f) {
writeInt(Float.floatToIntBits(f));
}
void writeLong(long l) {
writeInt((int) (l >> 32));
writeInt((int) l);
}
void writeInt(int i) {
write(i >> 24);
write(i >> 16);
write(i >> 8);
write(i);
}
void writeShort(int i) {
write(i >> 8);
write(i);
}
final void write(int i){ writeByte(i); }
void writeByte(int i) { try {
ensureWriteMode();
{ if (outputStream != null) outputStream.write(i); }
byteCounter++;
} catch (Exception __e) { throw rethrow(__e); } }
void writeASCII(char c) {
write(toASCII(c));
}
void writeASCII(String s) {
write(toASCII(s));
}
// write/verify constant ASCII text
void exchangeASCII(String s) {
exchangeConstantBytes(toASCII(s));
}
void exchangeConstantBytes(byte[] data) {
for (int i = 0; i < l(data); i++)
exchangeByte(data[i]);
}
long readLong() {
long i = ((long) readInt()) << 32;
return i | (readInt() & 0xFFFFFFFFL);
}
float readFloat() {
return Float.intBitsToFloat(readInt());
}
int readInt() {
int i = read() << 24;
i |= read() << 16;
i |= read() << 8;
return i | read();
}
short readShort() {
int i = read() << 8;
return (short) (i | read());
}
byte[] readBytes(int n) {
byte[] data = new byte[n];
for (int i = 0; i < n; i++) {
int b = read();
if (b < 0)
throw fail("EOF");
data[i] = (byte) b;
}
return data;
}
String readString() {
int n = readInt();
if (eof()) return null;
return fromUtf8(readBytes(n));
}
// null is written as empty string
// writes UTF8 length (4 bytes) plus string as UTF8
void writeString(String s) {
byte[] utf8 = toUtf8(unnull(s));
writeInt(l(utf8));
write(utf8);
}
// -1 for EOF
final int read(){ return readByte(); }
int readByte() { try {
ensureReadMode();
++byteCounter;
int b = inputStream.read();
if (b < 0) eof = true;
return b;
} catch (Exception __e) { throw rethrow(__e); } }
void ensureReadMode() {
if (!readMode) throw fail("Not in read mode");
}
void ensureWriteMode() {
if (!writeMode) throw fail("Not in write mode");
}
// exchange = read or write depending on mode
void exchangeByte(byte getter, IVF1 setter) {
exchangeByte(() -> getter, setter);
}
void exchangeByte(IF0 getter, IVF1 setter) {
if (writeMode())
writeByte(getter.get());
if (readMode())
setter.get(toUByte(readByte()));
}
void exchangeShort(IF0 getter, IVF1 setter) {
if (writeMode())
writeShort(getter.get());
if (readMode())
setter.get(readShort());
}
void exchangeLong(IVar var) {
exchangeLong(var.getter(), var.setter());
}
void exchangeLong(IF0 getter, IVF1 setter) {
if (writeMode())
writeLong(getter.get());
if (readMode())
setter.get(readLong());
}
void exchangeByte(byte i) {
exchangeByte(() -> i, j -> assertEquals(i, j));
}
void exchangeInt(int i) {
exchangeInt(() -> i, j -> assertEquals(i, j));
}
void exchangeInt(IF0 getter, IVF1 setter) {
if (writeMode())
writeInt(getter.get());
if (readMode())
setter.get(readInt());
}
void exchange(ByteIO writable) {
if (writable != null) writable.readWrite(this);
}
void exchangeAll(Iterable extends ByteIO> writables) {
if (writables != null)
for (var writable : writables)
exchange(writable);
}
// write size in bytes of element first (as int),
// then the element itself.
// upon reading, size is actually ignored.
void exchangeWithSize(ByteIO writable) {
if (writeMode()) {
byte[] data = writable.saveToByteArray();
writeInt(l(data));
write(data);
}
if (readMode()) {
int n = readInt();
writable.readWrite(this);
}
}
void finish() {}
public void close() {
main.close(inputStream);
main.close(outputStream);
finish();
}
}
// you still need to implement hasNext() and next()
static abstract class IterableIterator implements Iterator, Iterable {
public Iterator iterator() {
return this;
}
public void remove() {
unsupportedOperation();
}
}
// Each instance is for one coin
static class G22DriftSystem extends MetaWithChangeListeners {
final public G22DriftSystem setCryptoCoin(String cryptoCoin){ return cryptoCoin(cryptoCoin); }
public G22DriftSystem cryptoCoin(String cryptoCoin) { this.cryptoCoin = cryptoCoin; return this; } final public String getCryptoCoin(){ return cryptoCoin(); }
public String cryptoCoin() { return cryptoCoin; }
String cryptoCoin;
// Position currently taken on platform (in crypto coin units)
// Positive for long position, negative for short position,
// zero for no position.
final public G22DriftSystem setDriftOnPlatform(double driftOnPlatform){ return driftOnPlatform(driftOnPlatform); }
public G22DriftSystem driftOnPlatform(double driftOnPlatform) { this.driftOnPlatform = driftOnPlatform; return this; } final public double getDriftOnPlatform(){ return driftOnPlatform(); }
public double driftOnPlatform() { return driftOnPlatform; }
double driftOnPlatform;
// Drift value we want to have
final public G22DriftSystem setTargetDrift(double targetDrift){ return targetDrift(targetDrift); }
public G22DriftSystem targetDrift(double targetDrift) { this.targetDrift = targetDrift; return this; } final public double getTargetDrift(){ return targetDrift(); }
public double targetDrift() { return targetDrift; }
double targetDrift;
final public G22DriftSystem setMarket(IFuturesMarket market){ return market(market); }
public G22DriftSystem market(IFuturesMarket market) { this.market = market; return this; } final public IFuturesMarket getMarket(){ return market(); }
public IFuturesMarket market() { return market; }
transient IFuturesMarket market;
public transient FieldVar varCoinParams_cache;
public FieldVar varCoinParams() { if (varCoinParams_cache == null) varCoinParams_cache = varCoinParams_load(); return varCoinParams_cache;}
public FieldVar varCoinParams_load() {
return new FieldVar(this, "coinParams", () -> coinParams(), coinParams -> coinParams(coinParams)); }
final public G22DriftSystem setCoinParams(FutureCoinParameters coinParams){ return coinParams(coinParams); }
public G22DriftSystem coinParams(FutureCoinParameters coinParams) { if (!eq(this.coinParams, coinParams)) { this.coinParams = coinParams; change(); } return this; }
final public FutureCoinParameters getCoinParams(){ return coinParams(); }
public FutureCoinParameters coinParams() { return coinParams; }
FutureCoinParameters coinParams;
Set activeStrategies = syncLinkedHashSet();
double calculateTargetDrift() {
double drift = 0;
for (var strat : cloneList(activeStrategies))
drift += strat.drift();
targetDrift(drift);
return drift;
}
}
static class TimestampRange implements Comparable , IFieldsToList{
static final String _fieldOrder = "start end";
Timestamp start;
Timestamp end;
TimestampRange() {}
TimestampRange(Timestamp start, Timestamp end) {
this.end = end;
this.start = start;}
public boolean equals(Object o) {
if (!(o instanceof TimestampRange)) return false;
TimestampRange __1 = (TimestampRange) o;
return eq(start, __1.start) && eq(end, __1.end);
}
public int hashCode() {
int h = -2020922905;
h = boostHashCombine(h, _hashCode(start));
h = boostHashCombine(h, _hashCode(end));
return h;
}
public Object[] _fieldsToList() { return new Object[] {start, end}; }
TimestampRange(long start, long end) {
this(new Timestamp(start), new Timestamp(end));
}
final Timestamp startTime(){ return start(); }
Timestamp start() { return start; }
final Timestamp endTime(){ return end(); }
Timestamp end() { return end; }
final TimestampRange startTime(Timestamp start){ return start(start); }
TimestampRange start(Timestamp start) { this.start = start; return this; }
final TimestampRange endTime(Timestamp end){ return end(end); }
TimestampRange end(Timestamp end) { this.end = end; return this; }
boolean complete() { return start != null && end != null; }
Duration duration() { return !complete() ? null : end.minusAsDuration(start); }
long length() { return !complete() ? 0 : end.unixDate()-start.unixDate(); }
public String toString() { return start + " to " + end; }
public int compareTo(TimestampRange r) {
return cmp(start, r.start);
}
List toList() { return ll(start, end); }
}
abstract static class AbstractJuicer extends MetaWithChangeListeners {
AbstractJuicer() {}
// what we are monitoring & closing (a position or a strategy)
public transient FieldVar varJuiceable_cache;
public FieldVar varJuiceable() { if (varJuiceable_cache == null) varJuiceable_cache = varJuiceable_load(); return varJuiceable_cache;}
public FieldVar varJuiceable_load() {
return new FieldVar(this, "juiceable", () -> juiceable(), juiceable -> juiceable(juiceable)); }
final public AbstractJuicer setJuiceable(Juiceable juiceable){ return juiceable(juiceable); }
public AbstractJuicer juiceable(Juiceable juiceable) { if (!eq(this.juiceable, juiceable)) { this.juiceable = juiceable; change(); } return this; }
final public Juiceable getJuiceable(){ return juiceable(); }
public Juiceable juiceable() { return juiceable; }
Juiceable juiceable;
public transient FieldVar varJuiceValue_cache;
public FieldVar varJuiceValue() { if (varJuiceValue_cache == null) varJuiceValue_cache = varJuiceValue_load(); return varJuiceValue_cache;}
public FieldVar varJuiceValue_load() {
return new FieldVar(this, "juiceValue", () -> juiceValue(), juiceValue -> juiceValue(juiceValue)); }
final public AbstractJuicer setJuiceValue(double juiceValue){ return juiceValue(juiceValue); }
public AbstractJuicer juiceValue(double juiceValue) { if (!eq(this.juiceValue, juiceValue)) { this.juiceValue = juiceValue; change(); } return this; }
final public double getJuiceValue(){ return juiceValue(); }
public double juiceValue() { return juiceValue; }
double juiceValue;
public transient FieldVar varClosingAllowed_cache;
public FieldVar varClosingAllowed() { if (varClosingAllowed_cache == null) varClosingAllowed_cache = varClosingAllowed_load(); return varClosingAllowed_cache;}
public FieldVar