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)