Module: compute_offers.py

"""
This module contains an algorithm for computing an offer from one player X for another Y.
The player Y has asked for a swap.

For the first demonstration, the algorithm is shared between the players althought in any
real application, each player would probably adjust the algorithm in accordance with
their understanding of the competitive negotiation.

For the demonstration, the offers differ because the players have individual predictions
about the future of the stock market and individual requirements for a deal. These
individual requirements are passed to the module through a calling parameter.

For the demo, there is a requirement that the caller has set the PATH so that is locates
the experience file belonging to X as well as the general rules file.

"""

import sys
print "in module compute_offers", sys.path

### following appends to system path are only for testing as a main program
##sys.path.append( "/Users/pbaker/Documents/Office Projects/Software Dev/demo1/playerB")
##sys.path.append( "/Users/pbaker/Documents/Office Projects/Software Dev/demo1/broker")


import experience as exp
import general_rules as rules
import current_market_terms as terms
import broker_model as broker
import random

random.seed()


def create_offers(X, Y, estimate, f, g):
    """
Creae offers and write them in text and Python expression format. 

The parameters of this function are:
   X = a string containing the indentifier of the party that is offering the swap
   Y = a string containing he identifier of the party asking for the swap
   estimate = X's market estimate
   f = file handle where text format should be written
   g = file handle where Python expressions should be written. 

The algorithm works like this. We use player X's estimate of the trends in stock prices
to predict a profit for X on this swap. That profit may or may not meet expectations.
However, we should also figure in the cash per share that comes at the start of the swap.
That amount is something we can ask at the start of the offer. For that reason, we
then calculate a range of asking values for the cash_per_share. These become part of the
offer.

Let us note here that the broker is entitled to a commission. If a deal is concluded,
we as the offerer will tell the broker what the comission should be so that the broker
can try to bill that to the asker. The broker can't compute the broker's fee from the
encrypted agreement. 

   """

    # respect the black list
    if Y in exp.blackList:
        f.write("# blacklisted \n")
        g.write("\n")
        return

    # respect experience based limits on this player
    if exp.swapValueLimit.has_key(Y):
        valueLimit = exp.swapValueLimit[Y]
    else:
        valueLimit = None
    if exp.swapIntervalLimit.has_key(Y):
        intervalLimit = exp.swapIntervalLimit[Y]
    else:
        intervalLimit = None
    if exp.swapHandicap.has_key(Y):
        handicap = exp.swapHandicap[Y]
    else:
        handicap = 1.0
    
    for swap_stock in terms.company_symbols:
        # swap_stock is the "have" stock of the asker and the "accept" stock of the offerer
        for for_stock in terms.company_symbols:
            # for_stock is the "want" stock of the asker and the "for" stock of the offerer
            if swap_stock == for_stock:
                continue
            # apply any blocking instructions
            if swap_stock in rules.block_accept_stock:
                continue
            if for_stock in rules.block_for_stock:
                continue
            
            
            print "offer %s in exhcange for asker's  %s" % (for_stock, swap_stock )
            for interval in terms.intervals:
                if interval > rules.max_interval:
                    continue
                if interval > intervalLimit:
                    continue
                
                # gather the facts and the prediction of stock prices
                for_shares = terms.exchange_rates[swap_stock][for_stock]
                swap_price = terms.prices[swap_stock]
                for_price= terms.prices[for_stock]
                future_swap_price = ((1.0 + (interval * estimate.trends[swap_stock]/100.0) ) *
                   swap_price)
                future_for_price = ((1.0 + (interval * estimate.trends[for_stock]/100.0) ) *
                   for_price)
                # at the end of interval, offerer holds swap_stock and sends shares back in exchange for for_stock
                # so offerer makes a profit if swap_stock increases the value of the holding
                delta_price_per_share =  future_for_price * for_shares - future_swap_price

##                #diagnostic
##                print "initial values %10.4f, %10.4f " % ( swap_price, for_shares * for_price)
##                print "final values %10.4f, %10.4f " % ( future_swap_price, for_shares * future_for_price)
##                print "for_shares %10.4f, swap rice %10.4f, for_price %10.4f" % (for_shares, swap_price, for_price)
##                print "future swap  %10.4f, future for  %10.4f, delta  %10.4f" % (future_swap_price, future_for_price, delta_price_per_share)
##                
                # formulate a goal
                profit_min_per_share = (handicap * rules.profit_rate_minimum/100.0) * interval * swap_price
                # determine the minimum cash_per_share
                min_cash_per_share = max( (profit_min_per_share - delta_price_per_share), 0.0)

                # diagnostic
                #print "profit rate %10.4f, " % rules.profit_rate_minimum
                #print "profit_min  %10.4f, min_cash  %10.4f" % ( profit_min_per_share, min_cash_per_share )
                
                # the maximum cash_per_share is determined from an arbitrarily high profit goal
                max_cash_per_share = max( handicap * 0.20 * interval * swap_price - delta_price_per_share, 0.0)
                # determine lower limit of shares
                min_num = rules.transaction_size_minimum / swap_price
                # determine upper limit of shares
                if valueLimit:
                    max_num = min( valueLimit / swap_price, rules.max_value  / swap_price)
                else:
                    max_num = rules.max_value  / swap_price
                    
                # pick an arbitrary reference number
                ref_id = 1 + int( 999999.0 * random.random())

                # begin offer record in files
                f.write("\noffer:\n")
                g.write("{ ")
                        
                # write offer
                g.write( "\"action\" : \"offer\", " )

                f.write("   accept: %s\n" % swap_stock)
                g.write("\"accept\" : \"%s\", " % swap_stock)

                f.write("   for: %s\n" % for_stock)
                g.write("\"for\" : \"%s\", " % for_stock)

                f.write("   cash_per_share: [%10.4f, %10.4f]\n" % (min_cash_per_share, max_cash_per_share) )
                g.write("\"cash_per_share\" : [%10.4f, %10.4f], " % (min_cash_per_share, max_cash_per_share) )

                f.write("   number: [%d, %d]\n" % (min_num, max_num) )
                g.write("\"number\" : [%d, %d], " % (min_num, max_num))

                f.write("   interval: %d\n" % interval)
                g.write("\"interval\" : %d, " % interval)

                # last element of offer. note no comma
                f.write("   ref_id: %d\n" % ref_id)
                g.write("\"ref_id\" : %d " % ref_id)
                
                # end offer record in files
                f.write("end\n")
                g.write("}\n")




if __name__ == '__main__':
    # n.b. to run this successfully, uncomment out the part that adda to the
    # module path

    f = open("testoffer.txt", "w")
    g = open("testoffer.py", "w")

    import market_estimate as estimate
    e = estimate.MarketEstimate(1.0)
    create_offers("B", "A", e, f, g)
    print "done"
    
    f.close()
    g.close()