Overall Statistics
Total Trades
799
Average Win
3.76%
Average Loss
-2.22%
Compounding Annual Return
50.927%
Drawdown
56.100%
Expectancy
0.250
Net Profit
406.052%
Sharpe Ratio
0.78
Loss Rate
54%
Win Rate
46%
Profit-Loss Ratio
1.69
Alpha
-0.332
Beta
56.437
Annual Standard Deviation
0.576
Annual Variance
0.332
Information Ratio
0.756
Tracking Error
0.576
Treynor Ratio
0.008
Total Fees
$146691.44
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")


from System import *
from QuantConnect import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Market import *
from QuantConnect.Indicators import *
from QuantConnect.Orders import *
from QuantConnect.Brokerages import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Algorithm.Framework.Selection import *


class MovingAverageCrossAlpha(AlphaModel):

    def __init__(self, fast_ma_len, slow_ma_len, prices=None, resolution=Resolution.Daily, algorithm=None):
        self.Name = "MovingAverage"

        self.ma_fast_len = fast_ma_len
        self.ma_slow_len = slow_ma_len
        self.resolution = Resolution.Daily
        self.ma_fast = RollingWindow[float](
            self.ma_fast_len)  # OHLCLoader.get_historical_RollingWindow__last_N_days(float, self.ma_fast_len, self.Log)
        self.ma_slow = RollingWindow[float](
            self.ma_slow_len)  # OHLCLoader.get_historical_RollingWindow__last_N_days(float, self.ma_slow_len, self)

        if prices is not None:
            for p in prices[-self.ma_fast_len:]:
                self.ma_fast.Add(p[1][3])

            for p in prices[-self.ma_slow_len:]:
                self.ma_slow.Add(p[1][3])

        self.prev_insight_direction = InsightDirection.Flat

        self.insight_time_span = 0

        if resolution == Resolution.Daily:
            self.insight_time_span = 60 * 24 - 1
        elif resolution == Resolution.Hour:
            self.insight_time_span = 60 - 1
        elif resolution == Resolution.Minute:
            self.insight_time_span = 1 - 0.01

        if algorithm is not None:
            # algorithm.Log(f"self.ma_slow.Count: {self.ma_slow.Count}")
            algorithm.Log(f"MovingAverage init DONE -> {self.ma_fast_len} / {self.ma_slow_len} ({self.get_ma_fast() } -> { self.get_ma_slow()})")

    def get_ma_slow(self):
        if self.ma_slow.Count < self.ma_slow_len:
            return None

        return sum(self.ma_slow) / self.ma_slow.Count

    def get_ma_fast(self):
        if self.ma_fast.Count < self.ma_fast_len:
            return None

        return sum(self.ma_fast) / self.ma_fast.Count

    def Update(self, algorithm, data):
        insights = []
        period = TimeSpan.FromMinutes(self.insight_time_span)
        magnitude = 1

        close = data["BTCUSD"].Close

        self.ma_fast.Add(close)
        self.ma_slow.Add(close)

        ma_slow = self.get_ma_slow()
        ma_fast= self.get_ma_fast()

        if ma_slow is None or ma_fast is None:
            return []

        is_Long = close > ma_slow and close > ma_fast
        is_Short = close < ma_slow and close < ma_fast


        direction = self.prev_insight_direction
        if is_Long:
            direction = InsightDirection.Up
        elif is_Short:
            direction = InsightDirection.Down

        # algorithm.Log(f"{direction}")

        insights.append(Insight.Price("BTCUSD", period, direction, magnitude))

        algorithm.Log(f"MA Cross {self.ma_fast_len} / {self.ma_slow_len} :: direction == {direction} -> {ma_fast} / {ma_slow}")

        return insights

    def OnSecuritiesChanged(self, algorithm, changes):
         pass
from datetime import datetime, timedelta
from pytz import utc
UTCMIN = datetime.min.replace(tzinfo=utc)


class EqualInsightWeightingPortfolioConstructionModel(PortfolioConstructionModel):
    def __init__(self, resolution = Resolution.Daily):
        '''Initialize a new instance of EqualWeightingPortfolioConstructionModel
        Args:
            resolution: Rebalancing frequency'''
        self.insightCollection = InsightCollection()
        self.removedSymbols = []
        self.nextExpiryTime = UTCMIN
        self.rebalancingTime = UTCMIN
        self.rebalancingPeriod = Extensions.ToTimeSpan(resolution)

    def CreateTargets(self, algorithm, insights):
        '''Create portfolio targets from the specified insights
        Args:
            algorithm: The algorithm instance
            insights: The insights to create portoflio targets from
        Returns:
            An enumerable of portoflio targets to be sent to the execution model'''

        targets = []

        if (algorithm.UtcTime <= self.nextExpiryTime and
            algorithm.UtcTime <= self.rebalancingTime and
            len(insights) == 0 and
            self.removedSymbols is None):
            return targets

        self.insightCollection.AddRange(insights)

        # Create flatten target for each security that was removed from the universe
        if self.removedSymbols is not None:
            universeDeselectionTargets = [ PortfolioTarget(symbol, 0) for symbol in self.removedSymbols ]
            targets.extend(universeDeselectionTargets)
            self.removedSymbols = None

        # Get insight that haven't expired of each symbol that is still in the universe
        activeInsights = self.insightCollection.GetActiveInsights(algorithm.UtcTime)



        # give equal weighting to each insight
        
        insight_map = {}
        for insight in activeInsights:
            if insight.Symbol not in insight_map:
                insight_map[insight.Symbol] = {"count": 0, "result": 0}

            insight_map[insight.Symbol]['count'] += 1
            insight_map[insight.Symbol]['result'] += insight.Direction

        algorithm.Log(f"==================================================")
        algorithm.Log(f"=============PortfolioConstructionModel===========")
        algorithm.Log(f"==================================================")
        algorithm.Log(f"> algorithm.UtcTime:")
        algorithm.Log(f"> {algorithm.UtcTime}")
        algorithm.Log(f"> ---------------------")
        # algorithm.Log(f"==================================================")
        errorSymbols = {}
        for symbol in insight_map.keys():
            insight_result = insight_map[symbol]
            # algorithm.Plot("Chart", "Insight", insight_result['result'] / insight_result['count'])

            target_pct = insight_result['result'] / insight_result['count']
            target = PortfolioTarget.Percent(algorithm, symbol, target_pct)
            algorithm.Log("> target: %s ->  %.2f %%" % (symbol, 100 * target_pct))
            if not target is None:
                targets.append(target)
            else:
                errorSymbols[insight.Symbol] = insight.Symbol

        self.nextExpiryTime = self.insightCollection.GetNextExpiryTime()
        if self.nextExpiryTime is None:
            self.nextExpiryTime = UTCMIN

        self.rebalancingTime = algorithm.UtcTime + self.rebalancingPeriod


        algorithm.Log(f"==================================================")
        algorithm.Log(f"==========/end PortfolioConstructionModel=========")
        algorithm.Log(f"==================================================")
        return targets

    def OnSecuritiesChanged(self, algorithm, changes):
        '''Event fired each time the we add/remove securities from the data feed
        Args:
            algorithm: The algorithm instance that experienced the change in securities
            changes: The security additions and removals from the algorithm'''

        # Get removed symbol and invalidate them in the insight collection
        self.removedSymbols = [x.Symbol for x in changes.RemovedSecurities]
        self.insightCollection.Clear(self.removedSymbols)
from clr import AddReference
AddReference("System")
AddReference("QuantConnect.Common")
AddReference("QuantConnect.Algorithm")
AddReference("QuantConnect.Algorithm.Framework")


from System import *
from QuantConnect import *
from QuantConnect.Data.Consolidators import *
from QuantConnect.Data.Market import *
from QuantConnect.Indicators import *
from QuantConnect.Orders import *
from QuantConnect.Brokerages import *
from QuantConnect.Algorithm import *
from QuantConnect.Algorithm.Framework import *
from QuantConnect.Algorithm.Framework.Alphas import *
from QuantConnect.Algorithm.Framework.Execution import *
from QuantConnect.Algorithm.Framework.Portfolio import *
from QuantConnect.Algorithm.Framework.Risk import *
from QuantConnect.Algorithm.Framework.Selection import *


from ma_cross_alpha import MovingAverageCrossAlpha
from equal_insight_weighting_portfolio_construction import EqualInsightWeightingPortfolioConstructionModel


import decimal

class CustomFeeModel:
    def __init__(self, algorithm, custom_fee):
        self.algorithm = algorithm
        self.custom_fee = decimal.Decimal(custom_fee)

    def GetOrderFee(self, security, order):
        return security.Price * order.AbsoluteQuantity * self.custom_fee

### <summary>
### This demonstration alpha reads the DailyFx calendar and provides insights based upon
### the news' outlook for the root currency's(USD) associated pairs
### </summary>
class CustomAlgorithm(QCAlgorithmFramework):

    def Initialize(self):
        self.SetStartDate(2015, 1, 1)
        self.SetEndDate(2019, 1, 1)
        self.SetCash(100000)

        self.SetBrokerageModel(BrokerageName.Bitfinex, AccountType.Margin)
        self.resolution = Resolution.Daily
        self.symbol_name = "BTCUSD"
        self.security = self.AddSecurity(
            SecurityType.Crypto,
            self.symbol_name,
            self.resolution,
            Market.Bitfinex,
            False,
            2.1,
            False
        )

        # Flag to indicate whether critical error has occurred during strategy execution
        self.is_error = False

        # line below returns datetime timedelta for last 30 self.resolution timeframes
        # Extensions.ToTimeSpan(self.resolution) * 30
        tradeBarHistory = self.History([self.security.Symbol], Extensions.ToTimeSpan(self.resolution) * 30, self.resolution)

        self.prices = list(tradeBarHistory.iterrows())

        # remove today's data
        self.prices = self.prices[:len(self.prices) - 1]

        self.Log(f"history len - {len(self.prices)}")
        self.Log(f"history[0] - {self.prices[0]}")
        self.Log(f"history[-1] - {self.prices[-1]}")

        self.SetUniverseSelection(ManualUniverseSelectionModel([self.symbol_name]))

        alpha_list = []
        for i in range(8, 12):
            for j in range(20, 27):
                alpha_list.append(MovingAverageCrossAlpha(i, j, prices=self.prices, resolution=self.resolution, algorithm=self))

        # Set to use our FxCalendar Alpha Model
        self.SetAlpha(
            CompositeAlphaModel(
                *alpha_list
            )
        )

        # Default Models For Other Framework Settings
        self.SetPortfolioConstruction(EqualInsightWeightingPortfolioConstructionModel(resolution=self.resolution))
        self.SetExecution(ImmediateExecutionModel())
        self.SetRiskManagement(NullRiskManagementModel())