| 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"));
}
}
}