Overall Statistics |
Total Orders 864 Average Win 0.80% Average Loss -0.72% Compounding Annual Return -0.329% Drawdown 23.100% Expectancy -0.001 Start Equity 100000.00 End Equity 96544.44 Net Profit -3.456% Sharpe Ratio -0.208 Sortino Ratio -0.233 Probabilistic Sharpe Ratio 0.004% Loss Rate 52% Win Rate 48% Profit-Loss Ratio 1.10 Alpha -0.011 Beta -0.007 Annual Standard Deviation 0.057 Annual Variance 0.003 Information Ratio -0.707 Tracking Error 0.148 Treynor Ratio 1.622 Total Fees $0.00 Estimated Strategy Capacity $490000.00 Lowest Capacity Asset AUDUSD 5O Portfolio Turnover 15.39% |
#region imports from AlgorithmImports import * #endregion class RiskPremiaForexAlgorithm(QCAlgorithm): ''' Asymmetric Tail Risks and Excess Returns in Forex Markets Paper: https://arxiv.org/pdf/1409.7720.pdf ''' def initialize(self): self.set_start_date(2009, 1, 1) # Set Start Date self.set_end_date(2019, 9, 1) # Set End Date self.set_cash(100000) # Set Strategy Cash # Add forex data of the following symbols for pair in ['EURUSD', 'AUDUSD', 'USDCAD', 'USDJPY']: self.add_forex(pair, Resolution.HOUR, Market.FXCM) self._lookback = 30 # Length(days) of historical data self._next_rebalance = self.time # Next time to rebalance self._rebalance_days = 7 # Rebalance every 7 days (weekly) self._long_skew_level = -0.6 # If the skewness of a pair is less than this level, enter a long postion self._short_skew_level = 0.6 # If the skewness of a pair is larger than this level, enter a short position def on_data(self, data): ''' Rebalance weekly for each forex pair ''' # Do nothing until next rebalance if self.time < self._next_rebalance: return # Get historical close data for the symbols history = self.history(self.securities.keys(), self._lookback, Resolution.DAILY) history = history.drop_duplicates().close.unstack(level=0) # Get the skewness of the historical data skewness = self._get_skewness(history) long_symbols = [k for k,v in skewness.items() if v < self._long_skew_level] short_symbols = [k for k,v in skewness.items() if v > self._short_skew_level] # Liquidate the holdings for pairs that will not trade for holding in self.portfolio.values(): symbol = holding.symbol if holding.invested and symbol.value not in long_symbols + short_symbols: self.liquidate(symbol, 'Not selected pair') # Open positions for the symbols with equal weights count = len(long_symbols) + len(short_symbols) for pair in long_symbols: self.set_holdings(pair, 1/count) for pair in short_symbols: self.set_holdings(pair, -1/count) # Set next rebalance time self._next_rebalance += timedelta(self._rebalance_days) def _get_skewness(self, values): ''' Get the skewness for all forex symbols based on its historical data Ref: https://www.itl.nist.gov/div898/handbook/eda/section3/eda35b.htm ''' # Get the numerator of the skewness numer = ((values - values.mean()) ** 3).sum() # Get the denominator of the skewness denom = self._lookback * values.std() ** 3 # Return the skewness return (numer/denom).to_dict()