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

221
LINES

< > BotCompany Repo | #1036572 // NGSStrategy2 - "NGS" Strategy v2 #themoneymakingstrategy (not anymore though - now we have the Mystery Strategy etc)

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

Libraryless. Click here for Pure Java version (31812L/204K).

1  
// Design notes.
2  
//
3  
// NG stands for "Non-Garbage"
4  
//
5  
// !Design notes end
6  
7  
sclass NGSStrategy2 extends G22CandleBasedStrategyWithTrailingStop is IntegrityCheckable {
8  
  settable int period1 = 100; // the "slow" MA
9  
  settable int period2 = 5;   // the fast MA (for opening)
10  
  settable double maMinMove = 0.07; // MA trend threshold
11  
  settable double maMoveHysteresis = 0.01; // MA trend non-oscillation threshold
12  
13  
  settable Side longSide = new(1);
14  
  settable Side shortSide = new(-1);
15  
  
16  
    // false = only manual openLong() and openShort(),
17  
  // not based on indicators.
18  
  // true = auto-open positions indefinitely depending on
19  
  // indicators.
20  
  settable bool autoPositions = true;
21  
  
22  
  // temporal fields follow
23  
  
24  
  S fieldsToReset() {
25  
    ret lineCombine(super.fieldsToReset(), [[
26  
      ma1 ma2 lastMA1 maMove movingAverage1_cache movingAverage2_cache
27  
      maMoveHistory maDirection maDirectionHistory
28  
    ]]);
29  
  }
30  
  
31  
  settable double ma1;
32  
  settable double ma2;
33  
  settable double lastMA1 = Double.NaN;
34  
  settable double maMove;
35  
  settable int maDirection;
36  
  
37  
  // Too big, disabling this by default now
38  
  gettable TickerSequence maMoveHistory/* = new TickerSequence("maMove")*/;
39  
  gettable TickerSequence maDirectionHistory = new TickerSequence("maDirection");
40  
  
41  
  settable transient O visualizer;
42  
  
43  
  selfType emptyClone() {
44  
    selfType clone = cast super.emptyClone();
45  
    clone.longSide(longSide.emptyClone(clone));
46  
    clone.shortSide(shortSide.emptyClone(clone));
47  
    ret clone;
48  
  }
49  
  
50  
  // Side = logic for one side (long/short = direction 1/-1)
51  
  record noeq Side(int direction) {
52  
    // Our condition is:
53  
    // Price has to be in a band close to the MA first (band 1)
54  
    // and then move into a band further away from the MA (band 2)
55  
    
56  
    // Are we allowed to open positions in this direction at all?
57  
    settable bool enabled = true;
58  
    
59  
    // First band (close to MA)
60  
    settable DoubleRange band1 = new(0.2, 0.6);
61  
    
62  
    // Second band
63  
    settable DoubleRange band2 = new(0.6, 1.0);
64  
    
65  
    // Percentage to close the position at (below band/above band)
66  
    settable double closeLevel = 0;
67  
    
68  
    // Do we require the MA to go in our direction?
69  
    // (If no, we just require it to not go the other direction.)
70  
    settable bool needMADirection = true;
71  
    
72  
    // temporal fields follow
73  
    
74  
    selfType emptyClone(NGSStrategy2 stratClone) {
75  
      ret copyFields(this, stratClone.new Side(),
76  
        identifiers("direction enabled band1 band2 closeLevel"));
77  
    }
78  
    
79  
    // Has price visited band1 recently?
80  
    settable bool wasInBand1;
81  
    
82  
    // convert percent band to current price range
83  
    DoubleRange instantiateBand(DoubleRange band, double ma1 default ma1) {
84  
      ret doubleRange(
85  
        maPlusPercent(ma1, direction*band.start),
86  
        maPlusPercent(ma1, direction*band.end)).sort();
87  
    }
88  
    
89  
    double priceToPercent(double price, double ma1 default ma1) {
90  
      ret (price/ma1-1)*direction*100;
91  
    }
92  
    
93  
    double relativePrice() {
94  
      ret priceToPercent(currentPrice());
95  
    }
96  
    
97  
    void step {
98  
      var relativePrice = relativePrice();
99  
      
100  
      // close if too close to band/beyond band
101  
      if (relativePrice < closeLevel && sign(drift()) == direction) {
102  
        closeAllPositions(direction > 0 ? "Below band" : "Above band");
103  
      }
104  
      
105  
      // price on wrong side => reset
106  
      if (relativePrice < 0) wasInBand1(false);
107  
      
108  
      // Above band2 => reset
109  
      if (relativePrice > band2.end) wasInBand1(false);
110  
      
111  
      // We hit band 1
112  
      if (band1.contains(relativePrice)) wasInBand1(true);
113  
      
114  
      if (autoPositions && enabled && wasInBand1 && band2.contains(relativePrice)
115  
        && ma2*direction < currentPrice()*direction
116  
        && (needMADirection ? maDirection == direction : maDirection != -direction)) {
117  
        log("Trigger " + this);
118  
        wasInBand1(false);
119  
        if (drift() == 0 && !inCooldown())
120  
          openPosition(direction);
121  
      }
122  
    }
123  
 
124  
    // "short" or "long"   
125  
    toString { ret trading_directionToPositionType(direction); }
126  
    
127  
    LS status() {
128  
      ret listCombine(
129  
        enabled ? null : "disabled",
130  
        "Relative price: "+ formatDouble1(relativePrice()),
131  
        !wasInBand1 ? null : "Was in band 1",
132  
        "Band 1: " + band1,
133  
        "Band 2: " + band2,
134  
        "Close level: " + closeLevel
135  
      );
136  
    }
137  
  } // end of Side
138  
139  
  void ngsStep aka ngsLogic() {
140  
    //double close = currentPrice();
141  
    //double drift = drift();
142  
    
143  
    for (side : sides())
144  
      side.step();
145  
      
146  
    change();
147  
  }
148  
  
149  
  simplyCached MAIndicator movingAverage1() {
150  
    ret new MAIndicator(period1);
151  
  }
152  
  
153  
  simplyCached MAIndicator movingAverage2() {
154  
    ret new MAIndicator(period2);
155  
  }
156  
157  
  L<CandleBasedIndicator> indicators() {
158  
    ret ll(movingAverage1(), movingAverage2());
159  
  }
160  
161  
  bool maRising() { ret maDirection > 0; }
162  
  bool maFalling() { ret maDirection < 0; }
163  
  double moveSignal() { ret maMove/maMinMove*100; }
164  
  
165  
  double maPlusPercent(dbl ma1 default ma1, double percent) { ret ma1*(1+percent/100); }
166  
  double maMinusPercent(dbl ma1 default ma1, double percent) { ret ma1*(1-percent/100); }
167  
168  
  {
169  
    granularity(30);
170  
    
171  
    onNewPrice(price -> ngsStep());
172  
    onCandleCompleted(candle -> {
173  
      lastMA1(ma1);
174  
      ma1(movingAverage1()!);
175  
      ma2(movingAverage2()!);
176  
      maMove((ma1/lastMA1-1)*100);
177  
      maMoveHistory?.addIfPriceChanged(maMove, currentTime());
178  
      if (maMove >= maMinMove)
179  
        maDirection(1);
180  
      else if (maMove <= -maMinMove)
181  
        maDirection(-1);
182  
      else if (maDirection < 0 && maMove >= -maMinMove2()
183  
        || maDirection > 0 && maMove <= maMinMove2())
184  
        maDirection(0);
185  
      maDirectionHistory?.addIfPriceChanged(maDirection, currentTime());
186  
      ngsStep();
187  
    });
188  
  }
189  
  
190  
  double maMinMove2() { ret maMinMove-maMoveHysteresis; }
191  
  
192  
  LS status() {
193  
    ret listCombine(
194  
      "Min distance, MA min move: " + maMinMove + " (current move: " + formatDouble(maMove, 4) + ", direction: " + maDirection + ")",
195  
      "MA periods: " + period1 + " " + period2,
196  
      "Do: " + (!autoPositions ? "Only manual positions" : commaCombine(stringIf(doLongs(), "Longs"), stringIf(doShorts(), "Shorts"))),
197  
      "Moving averages: " + formatDouble_significant(ma1, 4) + ", " + formatDouble_significant(ma2, 4),
198  
      map(sides(), side -> firstToUpper(str(side)) + " side: " + commaCombine(side.status())),
199  
      super.status()
200  
    );
201  
  }
202  
  
203  
  void afterStep :: before {
204  
  }
205  
  
206  
  bool doShorts() { ret shortSide.enabled; }
207  
  bool doLongs() { ret longSide.enabled; }
208  
  
209  
  L<Side> sides() {
210  
    ret ll(longSide, shortSide);
211  
  }
212  
  
213  
  int candlesNeededBeforeOperational() { ret period1; }
214  
  
215  
  bool usingCells() { false; }
216  
  
217  
  public void integrityCheck {
218  
    movingAverage1().integrityCheck();
219  
    movingAverage2().integrityCheck();
220  
  }
221  
}

Author comment

Began life as a copy of #1036558

download  show line numbers  debug dex  old transpilations   

Travelled to 2 computer(s): mqqgnosmbjvj, wnsclhtenguj

No comments. add comment

Snippet ID: #1036572
Snippet name: NGSStrategy2 - "NGS" Strategy v2 #themoneymakingstrategy (not anymore though - now we have the Mystery Strategy etc)
Eternal ID of this version: #1036572/42
Text MD5: 8171b475658e80e436a42215660863c9
Transpilation MD5: 92e0f187d3228019ac821e357ef1d816
Author: stefan
Category: javax / trading
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2023-04-29 00:33:17
Source code size: 7045 bytes / 221 lines
Pitched / IR pitched: No / No
Views / Downloads: 258 / 475
Version history: 41 change(s)
Referenced in: [show references]