Overall Statistics |
Total Trades 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Net Profit 0% Sharpe Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -0.574 Tracking Error 0.103 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 |
namespace QuantConnect.Algorithm.CSharp { public class VentralQuantumRadiator : QCAlgorithm { private HeikinAshi hac; private URSI ursi; private SRSI srsi; private string ticker = "ETHUSD"; private RelativeStrengthIndex rsi; private RelativeStrengthIndex rsi_2; private MovingAverageConvergenceDivergence _macd; private MovingAverageConvergenceDivergence _macdex; private ExponentialMovingAverage _ema200; private ExponentialMovingAverage _ema200ex; private TradeBarConsolidator consolidator; bool invested = false; // in minutes int consolidation_period = 5; // candle counter private int max_period = 2; private int candle_counter = 0; private bool enable_candle_counter = false; private OrderTicket order = null; public override void Initialize() { SetStartDate(2021, 4, 15); //Set Start Date SetCash(100000); //Set Strategy Cash var _security = AddCrypto(ticker, Resolution.Minute, Market.Bitfinex); _security.SetFeeModel(new CustomFeeModel(this)); //AddEquity(ticker, Resolution.Minute); consolidator = new TradeBarConsolidator(TimeSpan.FromMinutes(consolidation_period)); consolidator.DataConsolidated += OnDataConsolidated; // SetBrokerageModel(BrokerageName.Bitfinex); hac = new HeikinAshi(); _macd = MACD(ticker, 12, 26, 9); _ema200 = EMA(ticker, 200); _ema200ex = _ema200.Of(hac); _macdex = _macd.Of(hac); RegisterIndicator(ticker, hac, consolidator); RegisterIndicator(ticker, _macdex, consolidator); RegisterIndicator(ticker, _ema200ex, consolidator); ursi = new URSI(this, 14, 21, 20, 3); srsi = new SRSI(this, 14, 14, 14, 3, 3); SetWarmup(200); } public override void OnWarmupFinished() { // load via historical data // value is 21 since largest value in URSI/SRSI is 21 var history = History(ticker, consolidation_period * 21, Resolution.Minute); foreach (var bar in history) consolidator.Update(bar); } public bool entry() { bool c1 = ursi.rsi1.getRSI() > ursi.rsi2.getRSI(); bool c2 = ursi.rsi1.getRSI() > ursi.bb_middle[0]; bool c3 = ursi.bb_middle[0] > ursi.bb_middle[2]; bool c4 = srsi.getK() > srsi.getD(); bool c5 = hac.Close > _macdex.Current.Value; bool c6 = _ema200ex.Current.Value < hac.Close; // do not trade on March 9th, 2018 for innaccurate data DateTime dt = DateTime.Now; //bool c5 = ((UtcTime.Month == 3) || (UtcTime.Month == 4)) && (UtcTime.Year == 2018); //Debug("Zulu"); return c1 && c2 && c3 && c4; } public bool exit() { bool c1 = ursi.bb_middle[0] < ursi.bb_middle[2]; bool c2 = srsi.getD() > 90; bool c3 = ursi.rsi1.getRSI() < ursi.rsi2.getRSI(); bool c4 = Securities[ticker].Holdings.UnrealizedProfitPercent > 0.03m; bool c5 = Securities[ticker].Holdings.UnrealizedProfitPercent < -0.03m; bool c6 = _macdex.Signal.Current.Value > 0; return (c3); } public void OnDataConsolidated(object sender, TradeBar consolidated) { //Debug(hac.Close); ursi.update(hac.Close); srsi.update(hac.Close); if (entry() && !invested && !Portfolio.Invested) { int quantity = (int)((Portfolio.Cash * 0.9m) / hac.Open); // market order Order(ticker, quantity); //Debug("Open: " + consolidated.Close + " : " + consolidated.Open + " : " + consolidated.High + " : " + consolidated.Low + " : " + consolidated.Price + " : " + UtcTime); // take profit // order = LimitOrder(ticker, quantity, Math.Round(hac.Open, 4)); candle_counter = 0; enable_candle_counter = true; Log("Position Entered: " + ticker + " at " + UtcTime); invested = true; } else if (exit() && Portfolio[ticker].Invested && order != null) // if you want to run market order remove && order !=null { //Debug("Close: " + consolidated.Close + " : " + consolidated.Open + " : " + consolidated.High + " : " + consolidated.Low + " : " + consolidated.Price + " : " + UtcTime); Log("Position Closed: " + ticker + " at " + UtcTime); Liquidate(ticker); // LimitOrder(ticker, -1 * Securities[ticker].Holdings.Quantity, Math.Round(hac.Open, 4)); candle_counter = 0; enable_candle_counter = true; order = null; invested = false; } // candle counter for un-filled candle if (enable_candle_counter && order != null && invested) { // check to see if filled bool filled = order.Status == OrderStatus.Filled; if (filled) { Log("Position Filled: " + ticker + " at " + UtcTime); enable_candle_counter = false; candle_counter = 0; } else { // check to see if hit max limit if (max_period <= candle_counter) { Log("Warning: Order Not Filled at " + UtcTime + " for " + ticker + " (Liquidated)"); Liquidate(ticker); order = null; enable_candle_counter = false; candle_counter = 0; invested = false; } else { // increment counter if not hit max candle_counter++; } } } } /// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. /// Slice object keyed by symbol containing the stock data public override void OnData(Slice data) { } } class URSI { public CorrectRSI rsi1; public CorrectRSI rsi2; public SimpleMovingAverage bb_sma; public RollingWindow<decimal> bb_middle; public URSI(QCAlgorithm qc, int rsi1_l, int rsi2_l, int bb_period, int bb_m_period) { this.rsi1 = new CorrectRSI(rsi1_l); this.rsi2 = new CorrectRSI(rsi2_l); this.bb_sma = new SimpleMovingAverage(bb_period); this.bb_middle = new RollingWindow<decimal>(bb_m_period); for (int i = 0; i < bb_m_period; i++) bb_middle.Add(0.0m); } public void update(decimal value) { rsi1.update(value); rsi2.update(value); bb_sma.update(rsi1.getRSI()); bb_middle.Add(bb_sma.getSMA()); } } class SRSI { public RollingWindow<decimal> min; public RollingWindow<decimal> max; public CorrectRSI rsi; public SimpleMovingAverage k; public SimpleMovingAverage d; public SRSI(QCAlgorithm qc, int rsi_l, int min_l, int max_l, int k, int d) { min = new RollingWindow<decimal>(min_l); max = new RollingWindow<decimal>(max_l); this.k = new SimpleMovingAverage(k); this.d = new SimpleMovingAverage(d); rsi = new CorrectRSI(rsi_l); for (int i = 0; i < min_l; i++) { min.Add(0.0m); max.Add(0.0m); } } public void update(decimal value) { rsi.update(value); min.Add(rsi.getRSI()); max.Add(rsi.getRSI()); k.update(getValue()); d.update(getK()); } public decimal getMin() { decimal min_value = Decimal.MaxValue; foreach (decimal i in min) { if (i < min_value) min_value = i; } return min_value; } public decimal getMax() { decimal max_value = 0.0m; foreach (decimal i in max) { if (i > max_value) max_value = i; } return max_value; } public decimal getValue() { if (getMax() - getMin() == 0) return 0.0m; return ((rsi.getRSI() - getMin()) / (getMax() - getMin())) * 100; } public decimal getK() { return k.getSMA(); } public decimal getD() { return d.getSMA(); } } class CorrectRSI { public decimal rsi = 0.0m; private RollingWindow<decimal> closes; private decimal gains; private decimal losses; private decimal period = 0.0m; public CorrectRSI(int period) { this.period = period; closes = new RollingWindow<decimal>(period); gains = 0.0m; losses = 0.0m; } public void update(decimal value) { closes.Add(value); if (!closes.IsReady) return; decimal diff = closes[0] - closes[1]; if (diff > 0) { gains = ((gains * (period - 1)) + diff) / period; losses = ((losses * (period - 1)) + 0.0m) / period; } else { gains = ((gains * (period - 1)) + 0.0m) / period; losses = ((losses * (period - 1)) + Math.Abs(diff)) / period; } if (losses == 0 || gains == 0) return; decimal rs = gains / losses; if (rs + 1 == 0) return; rsi = 100.0m - (100.0m / (1.0m + rs)); } public decimal getRSI() { return rsi; } } class MovingAverage { // variables: // QCAlgorithm variable QCAlgorithm algorithm { get; set; } // period of MovingAverage private int period { get; set; } // value of moving average private decimal ema { get; set; } // smoothing variable private decimal smoothing { get; set; } // constructors: // main constructor public MovingAverage(QCAlgorithm qc, int period, decimal init, decimal smoothing) { this.algorithm = qc; this.period = period; this.ema = init; this.smoothing = smoothing; } // methods: // update method public void update(decimal data) { decimal k = smoothing / (1 + period); ema = (data * k) + (ema * (1.0m - k)); } // ema get method public decimal getEMA() { return this.ema; } } class SimpleMovingAverage { private RollingWindow<decimal> values; private int period; public SimpleMovingAverage(int period) { this.values = new RollingWindow<decimal>(period); this.period = period; // init default values for (int i = 0; i < period; i++) values.Add(0.0m); } public void update(decimal value) { values.Add(value); } public decimal getSMA() { decimal sma = 0.0m; foreach (decimal d in values) sma += d; return sma / period; } } public class CustomFeeModel : FeeModel { private readonly QCAlgorithm _algorithm; public CustomFeeModel(QCAlgorithm algorithm) { _algorithm = algorithm; } public override OrderFee GetOrderFee(OrderFeeParameters parameters) { // custom fee math var fee = Math.Max( 1m, parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.0001m); //_algorithm.Log($"CustomFeeModel: {fee}"); return new OrderFee(new CashAmount(fee, "USD")); } } }