algorithmdepth-first-searchmaximum-profit-problem

Finding the path with max sum using DFS takes too long


At each time point agent can:

Given an array of prices, the aim is to find optimal open/close moments to get maximum profit.

I have implemented this, but the code runs very long (1 minute for 16 observations). What are the possibilities for the improvement?

Also, I want to get the optimal path itself. I can not store all the paths. What's the algorithm for backtracking?

Here is the code using Python:

import matplotlib.pyplot as plt
from collections import deque
import numpy as np
import time

def optimal_strategy(prices, root):
    s = deque([root])
    m = 0.0
    while s:
        n = s.pop()
        t = n['time'] + 1
        if t == len(prices):
            m = np.max([m, n['profit']])
            continue
        p = prices[t]
        s.append({'name': 'h' + str(t), 'time': t, 'parent': n['name'],
                  'funds': n['funds'], 'positions': n['positions'], 'profit': n['profit']})
        if p < n['funds']:
            s.append({'name': 'ol' + str(t), 'time': t, 'parent': n['name'],
                      'funds': n['funds'] - p, 'positions': n['positions'] + [p], 'profit': n['profit']})
        if len(n['positions']) > 0:
            s.append({'name': 'cl' + str(t), 'time': t, 'parent': n['name'],
                      'funds': n['funds'] + p, 'positions': n['positions'][1:], 'profit': n['profit'] + p - n['positions'][0]})

    return m

nobs = 16
np.random.seed(1)
prices = np.cumsum(np.random.normal(size=nobs))
plt.plot(prices)

t0 = time.time()
m = optimal_strategy(prices, {'name': 'r', 'time': -1, 'parent': None,
                              'funds': 4.0, 'positions': [], 'profit': 0.0})
print('Time {} Max {}'.format(time.time() - t0, m))

Solution

  • Here is an example showing how to get the exact trades that lead to the maximum funds. Maximizing funds guarantees that we maximized profits. Detailed calculations about which trades earned what profit can be calculated after we know the sequence.

    from collections import namedtuple
    
    Trade = namedtuple("Trade", "time funds shares parent")
    
    def optimal_strategy (prices, funds):
        root = Trade(-1, funds, 0, None)
        portfolios = [root]
        for i, price in enumerate(prices):
            # Start with the option of holding.
            next_portfolios = portfolios.copy()
            for portfolio in portfolios:
                # Can we sell?
                next_funds = portfolio.funds + price
                next_shares = portfolio.shares - 1
                if 0 <= next_shares and 0 <= next_funds:
                    if next_portfolios[next_shares].funds < next_funds:
                        next_portfolios[next_shares] = Trade(
                            i, next_funds, next_shares, portfolio)
                # Can we buy?
                next_funds = portfolio.funds - price
                next_shares = portfolio.shares + 1
                if 0 <= next_funds:
                    if len(next_portfolios) == next_shares:
                        next_portfolios.append(Trade(
                            i, next_funds, next_shares, portfolio))
                    elif next_portfolios[next_shares].funds < next_funds:
                        next_portfolios[next_shares] = Trade(
                            i, next_funds, next_shares, portfolio)
            portfolios = next_portfolios
    
            # Remove portfolios that cannot lead to our answer.
            while len(prices) - i < len(portfolios):
                portfolios.pop()
    
        path = []
        portfolio = portfolios[0]
        parent_portfolio = portfolio.parent
        while parent_portfolio is not None:
            if parent_portfolio.shares < portfolio.shares:
                path.append((portfolio.time, 'buy'))
            else:
                path.append((portfolio.time, 'sell'))
            portfolio = parent_portfolio
            parent_portfolio = portfolio.parent
        return list(reversed(path))