Overall Statistics
Total Trades
1568
Average Win
1.28%
Average Loss
-1.16%
Compounding Annual Return
331.626%
Drawdown
2.100%
Expectancy
0.577
Net Profit
10.292%
Sharpe Ratio
7.604
Probabilistic Sharpe Ratio
99.016%
Loss Rate
25%
Win Rate
75%
Profit-Loss Ratio
1.10
Alpha
1.915
Beta
-0.098
Annual Standard Deviation
0.247
Annual Variance
0.061
Information Ratio
5.654
Tracking Error
0.261
Treynor Ratio
-19.118
Total Fees
$12661.40
Estimated Strategy Capacity
$0
Lowest Capacity Asset
6J WSYSCWI3O1KX
import pytz
from math import floor

class FuturesTester(QCAlgorithm):
    
    def Initialize(self):
        self.SetTimeZone("America/Chicago")
        self.SetCash(10000000) 
        self.jpy = self.AddFuture(Futures.Currencies.JPY, Resolution.Tick) 
        self.jpy.SetFilter(0, 90)
        self.future = self.Securities[self.jpy.Symbol]
        self.SetExecution( ImmediateExecutionModel() )
        self.tz = pytz.timezone('America/Chicago')    
        
        csv = self.Download("https://www.dropbox.com/s/pvkazw9206w51am/QC_trades_data.csv?dl=1")
        data = csv.split("\n")
        self.b_trades = []
        self.s_trades = []
        for i in data:
            line = i.split(',')
            if line[0] == 'buy':
                self.b_trades.append({"direction": line[1], "time": datetime.fromtimestamp(float(line[2]), tz=self.tz)})
            elif line[0] == 'sell':
                self.s_trades.append({"direction": line[1], "time": datetime.fromtimestamp(float(line[2]), tz=self.tz)})
        
        start = min(self.b_trades[0]["time"], self.s_trades[0]["time"]) - timedelta(days=1)
        end = max(self.b_trades[-1]["time"], self.s_trades[-1]["time"]) + timedelta(hours=1)
        self.SetStartDate(start) 
        self.SetEndDate(2021, 10, 31)
        
        self.prev_day = start.day
        self.prev_time = None
        self.b_remaining = 0
        self.s_remaining = 0
        self.b_start = None
        self.s_start = None
        self.liquid_contract = None
        
        
    def OnData(self, slice):
        t = self.Time.replace(tzinfo=self.tz)
        
        if self.b_remaining < 0:
            self.get_liquid_contract(slice)
            # self.Debug("Volume: {} Remaining: {} Seconds since open: {}".format(self.liquid_contract.Volume, self.b_remaining, (self.Time - self.b_start).seconds))
            if self.liquid_contract.Volume > 1:
                order = -(self.liquid_contract.Volume - 1)
                self.b_remaining += order
                self.MarketOrder(self.liquid_contract.Symbol, order)
            
        elif self.s_remaining > 0:
            self.get_liquid_contract(slice)
            # self.Debug("Volume: {} Remaining: {} Seconds since open: {}".format(self.liquid_contract.Volume, self.s_remaining, (self.Time - self.s_start).seconds))
            if self.liquid_contract.Volume > 1:
                order = (self.liquid_contract.Volume - 1)
                self.s_remaining -= order
                self.MarketOrder(self.liquid_contract.Symbol, order)
                
        elif t >= self.b_trades[0]['time']:
            
            if self.b_trades[0]['direction'] == "b":
                if not self.Portfolio.Invested:
                    self.b_start = self.Time
                    self.get_liquid_contract(slice)
                    self.b_remaining = -self.get_number_of_contracts()
                    # self.Debug("b_trade open: {}".format(self.b_remaining))
                    # self.Debug(self.liquid_contract.Volume)
                    order = -(self.liquid_contract.Volume - 1)
                    self.b_remaining += order
                    self.MarketOrder(self.liquid_contract.Symbol, order)
                else:
                    self.Debug("Buy execution error - trade already open")
                    
            elif self.b_trades[0]['direction'] == "s":
                self.b_remaining = 0
                # self.Debug("b liquidate")
                self.Liquidate()
                
            del self.b_trades[0]
            
        elif t >= self.s_trades[0]['time']:
            
            if self.s_trades[0]['direction'] == "s":
                if not self.Portfolio.Invested:
                    self.s_start = self.Time
                    self.get_liquid_contract(slice)
                    self.s_remaining = self.get_number_of_contracts()
                    # self.Debug("s_trade open: {}".format(self.s_remaining))
                    # self.Debug(self.liquid_contract.Volume)
                    order = (self.liquid_contract.Volume - 1)
                    self.s_remaining -= order
                    self.MarketOrder(self.liquid_contract.Symbol, order)
                else:
                    self.Debug("Sell execution error - trade already open")
                    
            elif self.s_trades[0]['direction'] == "b":
                self.s_remaining = 0
                # self.Debug("s liquidate")
                self.Liquidate()
                
            del self.s_trades[0]
            
    # def OpenInterestSecurityInitializer(self, security):
    #     history = self.History([security.Symbol], timedelta(1))
    #     if 'openinterest' in history:
    #         oi = OpenInterest(self.Time, security.Symbol, history.openinterest.dropna().iloc[-1])
    #         security.SetMarketPrice(oi)
    
    # def get_trading_contract(self, slice):
    #     for chain in slice.FutureChains.Values:
    #         contracts = chain.Contracts.Values
            
    #         chainContracts = list(contracts)
    #         self.Debug(chainContracts[0])
    #         self.Debug(len(chainContracts))
    #         chainContracts = sorted(chainContracts, key=lambda x: x.Expiry)
            
    def get_number_of_contracts(self):
        return floor((self.Portfolio.MarginRemaining * 0.05) / self.future.BuyingPowerModel.InitialOvernightMarginRequirement)

    def OnMarginCallWarning(self):
        self.Error("You received a margin call warning..")
        
    def get_liquid_contract(self, slice):
        for chain in slice.FutureChains:
            self.popular_contracts = [contract for contract in chain.Value if contract.Expiry > self.Time + timedelta(days=5)]
            
            #2. If the length of contracts in this chain is zero, continue to the next chain
            if not len(self.popular_contracts):
                continue
            
            #3. Sort our contracts by open interest in descending order and save to sortedByOIContracts
            sorted_contracts = sorted(self.popular_contracts, key=lambda k : k.Expiry)
            
            #4. Save the contract with the highest open interest to self.liquidContract
            self.liquid_contract = sorted_contracts[0]