Stock Backtesting - mysql

I'm fairly new to MYSQL but I have experience programming in VBA. I'm trying to create a stored procedure which will backtest a strategy against a table of historical stock price data.The procedure will calculate the 20 Day moving average of closing stock price then if the selected day CLOSE has a price that his higher than the moving average a "buy" is initiated. If a trade is currently on, no new "buys" can be placed and if the selected day CLOSE is below the 20 day moving average a "sell" is initiated. Every time a buy or sell is placed the data is recorded in a table created called backtest results. The data I'm using is from http://finance.yahoo.com/q/hp?s=AAPL+Historical+Prices and I placed it into a table. As you can see the the data is in descending order so in order for me to back test I have to start at the earliest date which happens to be at the bottom. The problem I'm having is that no data is being inputted into this newly created table (Buy Date, Buy Price, Sell Date, Sell Price) . Can anyone spot why this is? Also is mysql suited for row-by-row processing? Thanks!
`Begin
DECLARE total_count int;
DECLARE moving_average decimal;
DECLARE move_up int;
DECLARE current_price decimal;
DECLARE count_var int;
DECLARE buy_price decimal;
DECLARE sell_price decimal;
DECLARE trade_on tinyint;
DECLARE account_balance decimal;
DECLARE start_row int;
DECLARE trade_date date;
SET #trade_on = 0;
SELECT #trade_on;
DROP TABLE IF EXISTS backtestresults;
CREATE TABLE backtestresults (buydate date,buyprice decimal, selldate date, sellprice decimal, accountbalance decimal);
SELECT COUNT(*) INTO #total_count FROM AAPL_Prices;
SELECT * FROM AAPL_Prices WHERE Special_ID = #total_count;
SET #start_row = #total_count -19;
SET #account_balance = 100000;
Loop1: WHILE (start_row > 1) DO
SELECT FORMAT(AVG(SClose),2) FROM AAPL_Prices WHERE Special_ID < #start_row + 19 AND Special_ID > #start_row INTO #moving_average;
SELECT SClose FROM AAPL_Prices WHERE Special_ID = #start_row -19 INTO #current_price;
SELECT TradeDate FROM AAPL_Prices WHERE special_ID = #start_row -19 INTO #trade_date;
IF #trade_on = 0 THEN
IF #current_price > #moving_average THEN
SET #buy_price = #current_price;
SET #trade_on = 1;
SET #account_balance = #account_balance - #buy_price;
INSERT INTO backtestresults (buyprice) values (#buy_price);
INSERT INTO backtestresults (buydate) values (#trade_date);
INSERT INTO backtestresults (balance) values (#account_balance);
End If;
END IF;
IF trade_on = 1 THEN
IF #current_price < #moving_average THEN
SET #sell_price = #current_price;
SET #trade_on = 0;
SET #account_balance = #account_balance + #buy_price;
INSERT INTO backtestresults(sellprice) values (#sell_price);
INSERT INTO backtestresults(selldate) values (#trade_date);
INSERT INTO backtestresults(balance) values (#account_balance);
END IF;
END IF;
SET #start_row = #start_row - 1;
END WHILE Loop1;
SELECT #account_balance;
DESCRIBE backtestresults;
SELECT #start_row;
End`

Related

Update and Insert table rows using a Mysql stored procedure

I have a table book_meetings which have 70000 record and I want to migrate this data into another table with little modification for this I have created a Mysql stored procedure. Records are inserted in new table but values are set as null.
I am selecting only four columns from book_meetings table and wants to insert them in table.
id int
date date
meet_at time
duration_in_hours decimal
What I want is calculate the start_date and end_date based on above values.
For example:
if date ="2017-09-08" , meet_at is "09:00:00" and duration_in_hours is 1.5
then start_date will be "2017-09-08 09:10:00"
end_date= start_date_duration_in_hour
end_date will be "2017-09-08 09:10:00"
start_date = concat date and meet_at
end_date = start_date + duration_in_hours
and insert this values in new table
if there is another better idea then please suggest
CREATE PROCEDURE book_meetings8()
BEGIN
-- Declare local variables
DECLARE done BOOLEAN DEFAULT 0;
DECLARE meet_at TIME;
DECLARE start_date DATETIME;
DECLARE tmp_date VARCHAR(255);
DECLARE end_date DATETIME;
DECLARE end_recurring_date DATE;
DECLARE date1 DATE ;
DECLARE id INTEGER(11);
DECLARE duration DECIMAL(8,2);
DECLARE minutes INTEGER(11);
-- Declare the cursor
DECLARE iter CURSOR
FOR
SELECT id,date, meet_at,duration_in_hours FROM
book_meetings LIMIT 100;
-- Declare continue handler
DECLARE CONTINUE HANDLER FOR SQLSTATE '02000' SET done=1;
-- Open the cursor
OPEN iter;
-- Loop through all rows
REPEAT
-- Get order number
FETCH iter INTO id,date1,meet_at,duration;
SET minutes = duration * 60;
SET start_date = CAST(date1 as char) + " "+CAST(meet_at as
char);
SET end_date = CAST(start_date as datetime) + INTERVAL
minutes MINUTE;
INSERT INTO
book_meetings_1(start_date,end_date)
VALUES(start_date,end_date);
-- End of loop
UNTIL done END REPEAT;
-- Close the cursor
CLOSE iter;
END;
Well I have solved above problem with single SQL statement (Insertion and updation all record at once without store procedure)
INSERT INTO temp_diary.book_meetings ( id,start_date,end_date) SELECT id,CONCAT(`date`, ' ', `meet_at`) as start_date,DATE_ADD(concat(date,' ',meet_at), INTERVAL `duration_in_hours` HOUR) as end_date FROM estate.book_meetings;

Update Whole Table adding value from previous row

Table acc
If I Edit "A" Amount to 100 then Total amount change
whole table need to update...
So What Will be the mysql query for updating whole table by adding (credit) or subtracting (debit) from previous total amount...
As commented by #Gordon Linoff, you can archive your goal using AFTER UPDATE trigger and a loop. I wrote up the idea as below for example.
DELIMITER //
CREATE TRIGGER acc_after_update
AFTER UPDATE
ON acc FOR EACH ROW
BEGIN
DECLARE done INT DEFAULT FALSE;
DECLARE type varchar(10);
DECLARE total_amount DEFAULT 0;
DECLARE name varchar(10)
DECLARE amount INT;
DECLARE cur1 CURSOR FOR SELECT name, type, amount FROM acc;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
OPEN cur1;
REPEAT
FETCH cur1 INTO name, type, amount;
IF NOT done THEN
CASE type
WHEN 'credit' THEN = total_amount + amount;
WHEN 'debit' THEN total_amount = total_amount - amount
END CASE
UPDATE acc
SET amount = total_amount
WHERE name = #name --not sure about this syntax
END IF;
UNTIL done END REPEAT;
CLOSE cur1;
END; //
DELIMITER ;
Hope this helps.
CREATE VIEW [dbo].[vSales]
AS
SELECT ROW_NUMBER() OVER(ORDER BY s.[Name] ) AS 'RowNo',
s.*
, CASE WHEN Type = 'Credit' THEN Amount ELSE - 1 * Amount END As NewAmount
FROM dbo.Sales AS s
GO
SELECT a.[RowNo],
a.[Name],
SUM(b.[NewAmount])
FROM dbo.vSales AS a INNER JOIN dbo.vSales AS b ON a.[RowNo] >= b.[RowNo]
GROUP BY a.[RowNo], a.[Name]
dbo.Sales is the table that holds all the values

stored procedure returns wrong value

I have a stored procedure that keeps giving me wrong answer. I asked the procedure to return the value of motor insurance. I run the procedure and give me the total of motor insurance premium but if I run it for the 4th time it give me the ageRange select statement value.
I moved the code into a new procedure but still the same.
My code
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `cal_motor_qoute`(in
coverID int , in dob date,
in sumMotor double , out QMsg varchar(200))
BEGIN
declare policy_cover , total , insRatio, ageExtra double;
declare ageRange int;
declare price_list varchar(200);
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
if (coverID = 1) then
set policy_cover = 0.002;
elseif (coverID = 2) then
set policy_cover = 0.0025;
elseif (coverID = 3) then
set policy_cover = 0.003;
elseif (coverID = 4) then
set policy_cover = 0.0035;
end if;
if ( ageRange < 25) then
set ageExtra = 0.0005;
else
set ageExtra = 0.000;
end if;
set insRatio = policy_cover + ageExtra;
set total = (sumMotor * insRatio )* 10;
set QMsg = concat('total Premium is: ',total);
select #QMsg;
END
Any help please..
SELECT DATEDIFF(NOW(),dob) / 365.25 AS ageRange from dual;
will not set the variable ageRange, but it will do a select (of the calculated value) and name the column of the resultset ageRange.
The (or rather: one) way to set the value of your variable is to use into:
SELECT DATEDIFF(NOW(),dob) / 365.25 into ageRange from dual;
Although this is probably not the most precise way to calculate the age of a person anyway. You might want to replace
if ( ageRange < 25) then
with
if ( dob > date_sub(now(), interval 25 year) ) then

Trigger not working:

I created trigger on table PENDING. Pending table has 3 columns - uniqueId , duration and maxDuration. I have another table COUNT with 2 columns - req_id, total
Here is my trigger--
CREATE TRIGGER plus3second BEFORE INSERT
ON PENDING
FOR EACH ROW
BEGIN
DECLARE req_id varchar(25);
DECLARE total int(11);
DECLARE duration int(2);
SET req_id = SUBSTR(new.uniqueId, 1, 14);
Select total into total from COUNT where req_id = 'req_id';
IF total > 100 THEN
SET duration = new.duration + 3;
IF duration < new.maxDuration Then
SET new.duration = duration;
END IF;
END IF;
END
Trigger created successfully. I fired these queries on COUNT and PENDING-
insert into COUNT values ('77711422099653',200);
insert into PENDING (uniqueId, duration, maxDuration) values ('77711422099653919893277163', 3, 20);
But trigger not working ...Where is the problem ?
Check with this trigger definition(with less conflicting names):
CREATE TRIGGER plus3second
BEFORE INSERT
ON PENDING
FOR EACH ROW
BEGIN
DECLARE tReqID varchar(25);
DECLARE tTotal int(11);
DECLARE tDuration int(2);
SET tReqID = SUBSTR(new.uniqueId, 1, 14);
SELECT total
INTO tTotal
FROM COUNT
WHERE req_id = tReqID;
IF tTotal > 100
THEN
SET tDuration = new.duration + 3;
IF tDuration < new.maxDuration
THEN
SET new.duration = tDuration;
END IF;
END IF;
END

MYSQL - function not returning expected results

I'm building a site for property rentals. I'm doing the search bit now and I'm trying to setup a function I can call for each property. The function needs to grab all rows from the rental_periods table attached to a given property then work out the best (cheapest) weekly price.
I have the following tables setup already.
properties - One line for each property
rental_periods - Multiple lines for each property, tied with id.
Each line is selfcatered or catered.
If selfcatered the price needs to be worked out from prices given in:
WeekDayPerDay - wdpd
WeekEndPerNight - wepn
Monthly price - monthly
Week price - wk
If catered the prices can be given in:
PerPersonPerNight - pppn
PerNight - pn
PerPersonPerWeek - pppw
I need a function that takes a property id and then grabs all periods that apply, then depending on selfcatered/catered works out the price per week that's best.
What I've got so far doesn't seem to be working. It either returns NULL or returns 100000.00 (my upper limit default price).
Here's the code
DELIMITER $$
CREATE FUNCTION get_price(myid INT)
RETURNS VARCHAR(20)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE price decimal(30,3) default 100000.000;
DECLARE id INT;
DECLARE prop_id INT;
DECLARE type enum('catered','selfcatered');
DECLARE name varchar(45);
DECLARE `from` date;
DECLARE `to` date;
DECLARE currency varchar(45);
DECLARE so tinyint;
DECLARE wk decimal(30,3);
DECLARE wepn decimal(30,3);
DECLARE wdpd decimal(30,3);
DECLARE monthly decimal(30,3);
DECLARE extra decimal(30,3);
DECLARE pppn decimal(30,3);
DECLARE pn decimal(30,3);
DECLARE pppw decimal(30,3);
DECLARE minstay int;
DECLARE maxstay int;
DECLARE breakfast varchar(45);
DECLARE annual TINYINT;
DECLARE cur1 CURSOR FOR SELECT * FROM rental_periods WHERE prop_id = myid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
REPEAT
FETCH cur1 INTO id, prop_id, type, name, `from`, `to`, currency, so, wk, wepn, wdpd, minstay, maxstay, monthly, extra, pppn, pn, pppw, breakfast, annual;
IF NOT done THEN
IF (#type = "selfcatered") THEN
IF (#wdpd > 0 AND (#wdpd * 7) < #price) THEN
SET price = #wdpd * 7;
END IF;
IF (#wepn > 0 AND (#wepn * 7) < #price) THEN
SET price = #wepn * 7;
END IF;
IF ((#wdpd > 0 AND #wepn > 0) AND
(#wdpd * 5 + #wepn * 2) < #price) THEN
SET price = #wdpd * 5 + #wepn * 2;
END IF;
IF (#monthly > 0 AND (#monthly / (52 / 12)) < #price) THEN
SET price = #monthly / (52 / 12);
END IF;
IF (#wk > 0 AND #wk < #price) THEN
SET price = #wk;
END IF;
ELSE
IF (#pppn > 0 AND (#pppn * 7) < #price) THEN
SET price = #pppn * 7;
END IF;
IF (#pn > 0 AND (#pn * 7) < #price) THEN
SET price = #pn * 7;
END IF;
IF (#pppw > 0 AND (#pppw) < #price) THEN
SET price = #pppw;
END IF;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE cur1;
RETURN price;
END $$
i'm hoping/not thats it's something stupid with how I've arranged it, or my lack of pure MySQL.
ANY help would be very helpful.
EDIT:
Here's an example row from rental_periods:
INSERT INTO `rental_periods` (`id`, `prop_id`, `type`, `name`, `from`, `to`, `currency`, `so`, `wk`, `wepn`, `wdpd`, `minstay`, `maxstay`, `monthly`, `extra`, `pppn`, `pn`, `pppw`, `breakfast`, `annual`)
VALUES (64732, 32, 'selfcatered', 'Summer', '2012-06-01', '2012-08-31', NULL, 1, '350', '60', '100', '', '', '', '', NULL, NULL, NULL, NULL, 0);
I'd expect the function to return 350 picked from the per week column. However if the wepn was 30, not 60, I'd expect 210 to come back (worked out from 7 * wepn prices).
The code im testing in SP:
DELIMITER $$
CREATE procedure tmp_get_price(myid INT)
BEGIN
DECLARE done INT DEFAULT 0;
DECLARE price decimal(30,3) default 100000.000;
DECLARE id INT;
DECLARE prop_id INT;
DECLARE type enum('catered','selfcatered');
DECLARE name varchar(45);
DECLARE `from` date;
DECLARE `to` date;
DECLARE currency varchar(45);
DECLARE so tinyint;
DECLARE wk decimal(30,3);
DECLARE wepn decimal(30,3);
DECLARE wdpd decimal(30,3);
DECLARE monthly decimal(30,3);
DECLARE extra decimal(30,3);
DECLARE pppn decimal(30,3);
DECLARE pn decimal(30,3);
DECLARE pppw decimal(30,3);
DECLARE minstay int;
DECLARE maxstay int;
DECLARE breakfast varchar(45);
DECLARE annual TINYINT;
DECLARE cur1 CURSOR FOR SELECT id, prop_id, type, name, `from`, `to`, currency, so, wk, wepn, wdpd, minstay, maxstay, monthly, extra, pppn, pn, pppw, breakfast, annual FROM rental_periods WHERE prop_id = myid;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;
OPEN cur1;
REPEAT
FETCH cur1 INTO id, prop_id, type, name, `from`, `to`, currency, so, wk, wepn, wdpd, minstay, maxstay, monthly, extra, pppn, pn, pppw, breakfast, annual;
IF NOT done THEN
IF (type = "selfcatered") THEN
IF (wdpd > 0 AND (wdpd * 7) < price) THEN
SET price = wdpd * 7;
END IF;
IF (wepn > 0 AND (wepn * 7) < price) THEN
SET price = wepn * 7;
END IF;
IF ((wdpd > 0 AND wepn > 0) AND
(wdpd * 5 + wepn * 2) < price) THEN
SET price = wdpd * 5 + wepn * 2;
END IF;
IF (monthly > 0 AND (monthly / (52 / 12)) < price) THEN
SET price = monthly / (52 / 12);
END IF;
IF (wk > 0 AND wk < price) THEN
SET price = wk;
END IF;
ELSE
IF (pppn > 0 AND (pppn * 7) < price) THEN
SET price = pppn * 7;
END IF;
IF (pn > 0 AND (pn * 7) < price) THEN
SET price = pn * 7;
END IF;
IF (pppw > 0 AND (pppw) < price) THEN
SET price = pppw;
END IF;
END IF;
END IF;
UNTIL done END REPEAT;
CLOSE cur1;
select price;
END $$
still doesnt work... :( am i being stupid... cant see why this wont work..?!?
gets the periods...
goes throught each one...
if the price is less set it....
select price....?!?
if i put multiple selects in... for example inside the cursor.
only the very bottom one fires and returns 100000.000
i've setup all the value fields as decimals and not allowing NULL...
any thoughts when im going wrong...? also tried debug by inserting in to log table... never fires..?!
Need to read more on cursors this seams a good place to start...
http://www.kbedell.com/2009/03/02/a-simple-example-of-a-mysql-stored-procedure-that-uses-a-cursor/