Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

348
LINES

< > BotCompany Repo | #1036196 // Corridor

JavaX fragment (include) [tags: use-pretranspiled]

Transpiled version (30017L) is out of date.

concept Corridor extends G22TradingStrategy {
  // how many cells the corridor has
  settableWithVar int capacity = 3;
  
  settableWithVar int maxPositionsPerDirection = 100;

  settableWithVar bool instantRestart = true;
  
  // initial position behavior
  settableWithVar bool openTwoPositionsAtStart;
  settableWithVar bool openRandomPositionAtStart = true;
  settableWithVar int openFixedDirectionAtStart; // 1 or -1 or 0
  
  // later position opening behavior
  settableWithVar bool alwaysOpenTwoPositions;
  
  // Pre-run to check if coin is in corridor mode (dev.)
  //settableWithVar bool usePreRun;
  //settableWithVar AbstractJuicer preRunJuicer;
  
  settableWithVar bool endStrategyWhenCapacityExceeded = true;
  
  settableWithVar bool useStrategyJuicer;
  !include #1036390 // MultiPullbackJuicer maker include
  settableWithVar SignalWithStrength closeSignal;
  
  // internal
  
  persistable class Position extends G22TradingStrategy.Position {
  }
  
  // direction: -1 or 1 for normal short or long
  // To change size of of position, use -2, 2 etc
  Position openPosition(int direction) {
    if (l(positionsInDirection(direction)) >= maxPositionsPerDirection) {
      log("Not opening new position in direction " + direction + ", max reached");
      null;
    }
    
    new Position p;
    p.marginToUse = marginPerPosition*abs(direction);
    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();
    startTime = currentTime();
    var priceCells = makePriceCells(startingPrice);
    print("Made price cells: " + priceCells);
    digitizer = new PriceDigitizer2(priceCells);
    digitizer.verbose(verbose);
    //digitizer.init(startingPrice);
    
    makeStrategyJuicer();
    
    log("Starting CORRIDOR at " + startingPrice + " +/- " + cellSize);
    
    startAction();
  }
  
  swappable void startAction {
    if (openTwoPositionsAtStart) {
      nextStep();
      openPosition(1);
      openPosition(-1);
      afterStep();
    } else if (openRandomPositionAtStart) {
      nextStep();
      log("Opening random initial position.");
      openPosition(securelyTossCoin() ? 1 : - 1);
      afterStep();
    } else if (openFixedDirectionAtStart != 0) {
      nextStep();
      log("Opening fixed initial position.");
      openPosition(sign(openFixedDirectionAtStart));
      afterStep();
    }
  }
  
  void price(double price) {
    if (currentPrice == price) ret;
    currentPrice = price;
    
    if (hasClosedItself()) ret;
    
    if (!started()) {
      if (primed) {
        primed(false);
        start();
      } else
        ret;
    }
    
    digitizer.digitize(price);
    direction = sign(digitizedPrice()-lastDigitizedPrice());
    
    if (applyStrategyJuicer()) ret;
    
    // No cell move?
    if (direction == 0) ret with afterStep();

    nextStep();
    //printWithPrecedingNL("New digitized price: " + digitizedPrice());

    if (started())
      step();
      
    print(this);
  }
  
  // true if exit
  bool applyStrategyJuicer() {
    // Check strategy juicer
    closeSignal(strongestSignal(strategyJuicer?.calculateCloseSignals()));
    if (closeSignal != null && closeSignal.isTrigger()) {
      log("Juicer close signal! " + closeSignal);
      closeOrRestart();
      afterStep();
      true;
    }
    
    false;
  }
  
  swappable void step {
    double p1 = lastDigitizedPrice();
    double p2 = digitizedPrice();
    direction = sign(p2-p1);
    if (direction == 0) ret;
    
    // Find winning positions to close
    
    L<G22TradingStrategy.Position> toClose = new L;
    for (p : direction > 0
      ? longPositionsAtOrBelowDigitizedPrice(p1)
      : shortPositionsAtOrAboveDigitizedPrice(p1)) {
      cast p to Position;
      p.closeReason("WIN");
      log("Closing winner position at " + formatPriceX(currentPrice) + " (" + p1 + " -> " + p2 + "): " + p);
      toClose.add(p);
    }
    
    // Dismantle/close corridor if too large
    double limit = fromCellNumber(toCellNumber(digitizedPrice())-capacity*direction);
    var toDismantle = direction > 0
      ? shortPositionsAtOrBelowDigitizedPrice(limit)
      : longPositionsAtOrAboveDigitizedPrice(limit);
    if (logToPrint) printVars(dismantleLimit := formatPriceX(limit),
      +direction,
      +capacity,
      +digitizedPrice(),
      +toDismantle);
    
    if (nempty(toDismantle)) {
      if (endStrategyWhenCapacityExceeded) {
        log("Capacity exceeded!");
        closeOrRestart();
        afterStep();
        ret;
      } else {
        for (p : toDismantle) {
          cast p to Position;
          p.closeReason("DISMANTLE");
          toClose.add(p);
        }
      }
    }
      
    //printVars("step", +p1, +p2, +direction, +closed, +opened);
    
    if (nempty(toClose))
      closePositions(toClose);

    // Apply take profits etc BEFORE possibly opening a new position
    afterStep();
    
    openNewPositionAfterMove();
      
    // Apply take profits, stop loss etc
    afterStep();
  }
  
  void closeOrRestart {
    if (instantRestart)
      restart();
    else
      closeMyself();
  }
  
  void restart {
    closeMyself();
    active(true);
    closedItself(0);
    startAction();
  }

  swappable void openNewPositionAfterMove {  
    // Open backward position here (if not there yet)
    openPositionIfNotThere(-direction);
      
    // Open forward position if configured as such
    if (alwaysOpenTwoPositions)
      openPositionIfNotThere(direction);
  }
  
  Position openPositionIfNotThere(int direction) {
    if (!hasPosition(digitizedPrice(), direction))
      ret openPosition(direction);
    else
      null;
  }
  
  swappable S mainDesc() {
    try answer renderCustomName();

    ret capacity + "x" + formatCellSize(cellSize) + " Corridor";
  }
  
  S baseToString() {
    ret colonCombine(
      _conceptID() == 0 ? "" : "Strategy " + _conceptID(),
      spaceCombine(
        squareBracketUnlessEmpty(areaDesc()),
        mainDesc(),
        nempty(cryptoCoin) ? "on " + cryptoCoin : ""
      ),
    );
  }
  
  LS status() {
    var r = currentCorridorPriceRange();
    ret listCombine(
      super.status(),
      "Max capacity: " + capacity,
      !started() ? null : "Current corridor: " + (r == null ? "-" : formatPrice(r.start) + " to " + formatPrice(r.end) + " (" + formatPercentage(currentCorridorSizeInPercent(), 3) + ")"),
      "Profit unit: " + formatMarginPrice(profitUnitBeforeAdversity()) + "/" + marginCoin + " " + formatMarginPrice(profitUnitAfterAdversity()) + ", loss unit: " + marginCoin + " " + formatMarginPrice(lossUnit()),
      closeSignal == null ?: "Close signal: " + closeSignal,
    );
  }
  
  // Corridor.toString
  toString {
    ret baseToString();
  } 
  
  S renderCustomName() {
    ret empty(customName) ? null : simpleQuoteOpt(trim(customName));
  }
  
  double maxCorridorPercentSize() {
    ret capacity*cellSize;
  }
  
  DoubleRange currentCorridorPriceRange() {
    double[] openingPrices = mapToDoubleArray(openPositions, ->.openingPrice);
    ret doubleRange(doubleMin(openingPrices), doubleMax(openingPrices));
  }
  
  double currentCorridorSizeInPercent() {
    DoubleRange r = currentCorridorPriceRange();
    ret r == null ? 0 : asPercentIncrease(r.end, r.start);
  }

  Position openShort() { ret openPosition(-1); }
  Position openLong() { ret openPosition(1); }
  
  {
    cellSize(3.0);
  }
  
  double positionSize() { ret marginPerPosition*leverage; }
  double profitUnitBeforeAdversity() { ret cellSize/100*positionSize(); }
  double profitUnitAfterAdversity() { ret (cellSize-adversity)/100*positionSize(); }
  double lossUnit() { ret (cellSize+adversity)/100*positionSize(); }
  
  // Uses profit calculated in profit units
  // (could alternatively use investment units, but like this the
  // loss ratio should stay more consistent between coins [=leverage values])
  class ProfitUnitsJuiceable is Juiceable {
    public double juiceValue() {
      ret coinProfit()/profitUnitBeforeAdversity();
    }
  }
  
  // Update strategy juicer if it exists
  void updateStrategyJuicer {
    if (strategyJuicer != null) {
      makeStrategyJuicer();
      applyStrategyJuicer();
    }
  }
  
  // Can also update existing juicer with new params
  void makeStrategyJuicer {
    if (!useStrategyJuicer) ret with strategyJuicer(null);
    var j = makeJuicer();
    j.copyTransientValuesFrom(strategyJuicer);
    j.juiceable(new ProfitUnitsJuiceable);
    strategyJuicer(j);
  }
  
  S fieldsToReset() {
    ret lines(ll(super.fieldsToReset(), [[closeSignal strategyJuicer]]));
  }
  
  double currentCellNumber() {
    ret toCellNumber(currentPrice());
  }
  
  // Cell we're in
  int currentDigitizedCell() {
    ret iround(toCellNumber(digitizedPrice()));
  }
  
  int priceDirection() {
    ret sign(currentCellNumber()-currentDigitizedCell());
  }
  
  // Cell we're currently moving to
  int targetCell() {
    ret currentDigitizedCell()+priceDirection();
  }
  
  double targetPrice() {
    ret fromCellNumber(targetCell());
  }
  
  // Coin profit when next target is reached
  double targetProfit() {
    ret coinProfitAtPrice(targetPrice());
  }
  
  // Is profit currently going up or down
  int profitDirection() {
    ret sign(targetProfit()-coinProfit());
  }
  
  // progress to next target in percent
  double progressInProfitDirection() {
    ret absDiff(currentCellNumber(), currentDigitizedCell())*100;
  }
}

download  show line numbers  debug dex  old transpilations   

Travelled to 2 computer(s): elmgxqgtpvxh, mqqgnosmbjvj

No comments. add comment

Snippet ID: #1036196
Snippet name: Corridor
Eternal ID of this version: #1036196/345
Text MD5: 8cfd09e821b755ad662489afd55962ee
Author: stefan
Category: javax / gazelle 22
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-12-06 21:56:41
Source code size: 9923 bytes / 348 lines
Pitched / IR pitched: No / No
Views / Downloads: 828 / 2254
Version history: 344 change(s)
Referenced in: #1003674 - Standard Classes + Interfaces (LIVE continued in #1034167)
#1036199 - Corridor [backup]
#1036204 - Corridor2 - the "analog" version that doesn't digitize prices [dev., have to figure out first]
#1036207 - Corridor [backup 2]
#1036257 - Corridor - backup used in the real-money experiment
#1036259 - Chaser
#1036402 - RunnerTradingAlgorithm