| Overall Statistics |
|
Total Orders 1990 Average Win 0.20% Average Loss -0.16% Compounding Annual Return -0.664% Drawdown 18.900% Expectancy -0.104 Start Equity 100000 End Equity 84444.73 Net Profit -15.555% Sharpe Ratio -1.702 Sortino Ratio -0.697 Probabilistic Sharpe Ratio 0.000% Loss Rate 61% Win Rate 39% Profit-Loss Ratio 1.28 Alpha -0.027 Beta -0.004 Annual Standard Deviation 0.016 Annual Variance 0 Information Ratio -0.421 Tracking Error 0.161 Treynor Ratio 7.438 Total Fees $8513.05 Estimated Strategy Capacity $750000.00 Lowest Capacity Asset USO THORT68ZZSYT Portfolio Turnover 21.42% |
# https://quantpedia.com/strategies/impact-of-eia-inventory-announcements-on-crude-oil-prices/
#
# The investment universe for this strategy is centered around the United States Oil Fund (USO) (an ETF that tracks the price of West Texas Intermediate Light
# Sweet Crude Oil). The USO is selected due to its direct correlation with crude oil prices and liquidity, making it suitable for intraday trading strategies.
# (Futures or CFDs are possible, although this has not been discussed.)
# (You can collect USO ETF data at a one-minute frequency from Refinitv Tick History.)
# Rationale: To react to crude oil price movements, particularly in response to EIA inventory announcements, which are known to influence market dynamics
# significantly, this approach leverages the observed momentum patterns and informed trading behaviors identified in the research.
# Big Picture Short Recapitulation Summary: For astute readers, in Section 5, the market timing strategy—trading the USO ETF—we focus on the third market
# half-hour (10:30-11:00) on Wednesdays. We use the EIA announcement to determine whether to go long or short during the final half-hour in USO.
# Strategy Execution: Performance of the intraday momentum strategy on EIA announcement days ONLY, i.e.,
# Taking a long (short) position at the beginning of the last half-hour interval, 15.30-16.00, if the return over the period 10.30-11.00 is positive (negative)
# and closing the position at the end of each trading day.
#
# QC implementation changes:
# region imports
from AlgorithmImports import *
# endregion
class ImpactOfEIAInventoryAnnouncementsOnCrudeOilPrices(QCAlgorithm):
def initialize(self) -> None:
self.set_start_date(2000, 1, 1)
self.set_cash(100_000)
self._observed_period: int = 30
self._trade_direction: Optional[int] = None
self._rebalance_flag: bool = False
zero_fees_flag: bool = False
security: Security = self.add_equity('USO', Resolution.MINUTE)
if zero_fees_flag:
security.set_fee_model(ConstantFeeModel(0))
self._traded_asset: Symbol = security.symbol
self.schedule.on(
self.date_rules.every(DayOfWeek.WEDNESDAY),
self.time_rules.at(11, 00),
self.trade_signal
)
self.schedule.on(
self.date_rules.every(DayOfWeek.WEDNESDAY),
self.time_rules.at(15, 30),
self.rebalance
)
def on_data(self, slice: Slice) -> None:
if not self._rebalance_flag:
return
self._rebalance_flag = False
if slice.contains_key(self._traded_asset) and slice[self._traded_asset] and self._trade_direction is not None:
self.market_order(
self._traded_asset,
self.calculate_order_quantity(self._traded_asset, self._trade_direction),
tag='MarketOrder'
)
self._trade_direction = None
def trade_signal(self) -> None:
history: DataFrame = self.history(self._traded_asset, self._observed_period)
if len(history) == self._observed_period:
self._trade_direction = 1 if history.close[-1] / history.close[0] - 1 > 0 else -1
def rebalance(self) -> None:
self._rebalance_flag = True
def on_order_event(self, orderEvent: OrderEvent) -> None:
order_ticket: OrderTicker = self.transactions.get_order_ticket(orderEvent.order_id)
symbol: Symbol = order_ticket.symbol
if orderEvent.status == OrderStatus.FILLED:
if 'MarketOrder' in order_ticket.tag:
self.market_on_close_order(symbol, -order_ticket.quantity)