| Overall Statistics |
|
Total Orders
1418
Average Win
0.54%
Average Loss
-0.71%
Compounding Annual Return
-0.410%
Drawdown
45.900%
Expectancy
-0.031
Start Equity
100000
End Equity
90074.06
Net Profit
-9.926%
Sharpe Ratio
-0.358
Sortino Ratio
-0.407
Probabilistic Sharpe Ratio
0.000%
Loss Rate
45%
Win Rate
55%
Profit-Loss Ratio
0.76
Alpha
-0.021
Beta
-0.051
Annual Standard Deviation
0.066
Annual Variance
0.004
Information Ratio
-0.364
Tracking Error
0.18
Treynor Ratio
0.463
Total Fees
$1074.23
Estimated Strategy Capacity
$0
Lowest Capacity Asset
CME_MP1.QuantpediaFutures 2S
Portfolio Turnover
2.21%
|
#region imports
from AlgorithmImports import *
#endregion
# Custom fee model
class CustomFeeModel(FeeModel):
def GetOrderFee(self, parameters):
fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
return OrderFee(CashAmount(fee, "USD"))
# Quantpedia data.
# NOTE: IMPORTANT: Data order must be ascending (datewise)
class QuantpediaFutures(PythonData):
_last_update_date: Dict[str, datetime.date] = {}
@staticmethod
def get_last_update_date() -> Dict[str, datetime.date]:
return QuantpediaFutures._last_update_date
def GetSource(self, config: SubscriptionDataConfig, date: datetime, is_live_mode: bool) -> SubscriptionDataSource:
return SubscriptionDataSource("data.quantpedia.com/backtesting_data/futures/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv)
def Reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> BaseData:
data = QuantpediaFutures()
data.Symbol = config.Symbol
if not line[0].isdigit(): return None
split: List[str] = line.split(';')
data.time = datetime.strptime(split[0], "%d.%m.%Y") + timedelta(days=1)
data['back_adjusted'] = float(split[1])
data['spliced'] = float(split[2])
data.value = float(split[1])
# store last update date
if config.symbol.value not in QuantpediaFutures._last_update_date:
QuantpediaFutures._last_update_date[config.symbol.value] = datetime(1,1,1).date()
if data.time.date() > QuantpediaFutures._last_update_date[config.symbol.value]:
QuantpediaFutures._last_update_date[config.symbol.value] = data.Time.date()
return data
# https://quantpedia.com/strategies/currency-momentum-factor/
#
# Create an investment universe consisting of several currencies (10-20). Go long three currencies with the highest 12-month momentum against USD
# and go short three currencies with the lowest 12-month momentum against USD. Cash not used as margin invest on overnight rates. Rebalance monthly.
import data_tools
from AlgorithmImports import *
class CurrencyMomentumFactor(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2000, 1, 1)
self.set_cash(100_000)
period: int = 12 * 21
self.set_warm_up(period, Resolution.DAILY)
tickers: List[str] = [
"CME_AD1", # Australian Dollar Futures, Continuous Contract #1
"CME_BP1", # British Pound Futures, Continuous Contract #1
"CME_CD1", # Canadian Dollar Futures, Continuous Contract #1
"CME_EC1", # Euro FX Futures, Continuous Contract #1
"CME_JY1", # Japanese Yen Futures, Continuous Contract #1
"CME_MP1", # Mexican Peso Futures, Continuous Contract #1
"CME_NE1", # New Zealand Dollar Futures, Continuous Contract #1
"CME_SF1" # Swiss Franc Futures, Continuous Contract #1
]
self._securities: List[Security] = []
self._traded_count: int = 3
for symbol in tickers:
data: Security = self.add_data(data_tools.QuantpediaFutures, symbol, Resolution.DAILY)
data.set_fee_model(data_tools.CustomFeeModel())
data.set_leverage(5)
data.roc: RateOfChange = self.ROC(symbol, period, Resolution.DAILY)
self._securities.append(data)
self._recent_month: int = -1
def on_data(self, slice: Slice) -> None:
if self.is_warming_up:
return
# rebalance monthly
if self.time.month == self._recent_month:
return
self._recent_month = self.time.month
perf: Dict[Symbol, float] = {
sec.symbol : sec.roc.current.value for sec in self._securities if
sec.roc.is_ready and
slice.contains_key(sec.symbol) and
slice[sec.symbol]
}
long: List[Symbol] = []
short: List[Symbol] = []
if len(perf) >= self._traded_count * 2:
sorted_by_performance: List[Symbol] = sorted(perf, key=perf.get, reverse=True)
long = sorted_by_performance[:self._traded_count]
short = sorted_by_performance[-self._traded_count:]
# trade execution
targets: List[PortfolioTarget] = []
for i, portfolio in enumerate([long, short]):
for symbol in portfolio:
targets.append(PortfolioTarget(symbol, ((-1) ** i) / len(portfolio)))
self.set_holdings(targets, True)