1

I have a df consisting of monthly share prices. I was hoping to find the optimal buy price and sell price to maximize earnings (revenue - costs). From research, it appears Scipy Optimize is the best tool to use, however all the examples I've seen do not show it being used with a dataframe.

A previous question sort of covered this. but I couldn't get it working for me, as my buy and sell quantities will change depending on the price. Meaning I need to recalc the df fields.

Any help would be greatly appreciated!

import pandas as pd
import numpy as np
import math
import datetime
from scipy.optimize import minimize

df = pd.DataFrame({
    'Time': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'Price': [44, 100, 40, 110, 77, 109, 65, 93, 89, 73]})

# Create Empty Columns
df[['Qty', 'Buy', 'Sell', 'Cost', 'Rev']] = pd.DataFrame([[0.00, 0.00, 0.00, 0.00, 0.00]], index=df.index)

# Initial Values
buy_price = 50
sell_price = 100

# Set Values at Time 0
df.at[0, 'Qty'] = 0
df.at[0, 'Buy'] = np.where(df.at[0, 'Price'] < buy_price, min(30 - df.at[0, 'Qty'], 10), 0)
df.at[0, 'Sell'] = np.where(df.at[0, 'Price'] > sell_price, min(df.at[0, 'Qty'], 10), 0)
df.at[0, 'Cost'] = df.at[0, 'Buy'] * df.at[0, 'Price']
df.at[0, 'Rev'] = df.at[0, 'Sell'] * df.at[0, 'Price']

# Set Remaining Values
for t in range(1, len(df)):
    df.at[t, 'Qty'] = df.at[t-1, 'Qty'] + df.at[t-1, 'Buy'] - df.at[t-1, 'Sell']
    df.at[t, 'Buy'] = np.where(df.at[t, 'Price'] < buy_price, min(30 - df.at[t, 'Qty'], 10), 0)
    df.at[t, 'Sell'] = np.where(df.at[t, 'Price'] > sell_price, min(df.at[t, 'Qty'], 10), 0)
    df.at[t, 'Cost'] = df.at[t, 'Buy'] * df.at[t, 'Price']
    df.at[t, 'Rev'] = df.at[t, 'Sell'] * df.at[t, 'Price']
2
  • for loop can do it Commented Oct 27, 2019 at 22:27
  • hi @WeNYoBen, thanks for your reply. could you please elaborate a bit further? I am not sure how to incorpoate a for loop, Should I first create a function to add fields (Qty, Buy, Sell, Cost, Rev) to my df and returns -1 * (revenue - cost). then use that in my scipy minimize optimisation. Commented Oct 27, 2019 at 23:25

1 Answer 1

1

So I ended up finding a way to use scipy optimize with my dataset.

I wrote a predicate which would allow me to solve my field calculations. then I called it in another function which I used for my scipy optimization.

This is not a very good optimization solution, as I don't seem to get very far from my initial guess. But it at least solved the question I had asked here.

import pandas as pd
import numpy as np
import scipy.optimize as optimize

df = pd.DataFrame({
    'Time': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    'Price': [44, 100, 40, 110, 77, 109, 65, 93, 89, 49]})

# Create Empty Columns
df[['Qty', 'Buy', 'Sell', 'Cost', 'Rev']] = pd.DataFrame([[0.00, 0.00, 0.00, 0.00, 0.00]], index=df.index)


class Predicate:
    def __init__(self):
        self.prev_time = -1
        self.prev_qty = 0
        self.prev_buy = 0
        self.prev_sell = 0
        self.Qty = 0
        self.Buy = 0
        self.Sell = 0
        self.Cost = 0
        self.Rev = 0

    def __call__(self, x):
        if x.Time == self.prev_time: # apply runs first row/column twice, hence this
            x.Qty = self.prev_qty
            x.Buy = self.prev_buy
            x.Sell = self.prev_sell
            x.Cost = x.Buy * x.Price
            x.Rev = x.Sell * x.Price
        else:
            x.Qty = self.prev_qty + self.prev_buy - self.prev_sell
            x.Buy = np.where(x.Price < buy_price, min(30 - x.Qty, 10), 0)
            x.Sell = np.where(x.Price > sell_price, min(x.Qty, 10), 0)
            x.Cost = x.Buy * x.Price
            x.Rev = x.Sell * x.Price
            self.prev_buy = x.Buy
            self.prev_qty = x.Qty
            self.prev_sell = x.Sell
            self.prev_time = x.Time
        return x


# Define function to minimize
def rev(params):
    global buy_price
    global sell_price
    buy_price, sell_price = params
    df2 = df.apply(Predicate(), axis=1)
    return -1 * (df2['Rev'].sum() - df2['Cost'].sum())


# Run optimization
initial_guess = [40, 90]
result = optimize.basinhopping(func=rev, x0=initial_guess, niter=1000, stepsize=10)
print(result.x)

# Run the final results
result.x = buy_price, sell_price
df = df.apply(Predicate(), axis=1)
print(df)
print(df['Rev'].sum() - df['Cost'].sum())
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.