Overall Statistics
/*
 * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
 * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Globalization;
using QuantConnect.Data;
using QuantConnect.Data.Market;

namespace QuantConnect.Algorithm.CSharp
{
    public class InterestAlgorithm : QCAlgorithm
    {
        QuantConnect.Symbol soybn;
        private Dictionary<string,Interest> interestList = new Dictionary<string,Interest>();
        /// <summary>
        /// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
        /// </summary>
        public override void Initialize()
        {
            SetStartDate(2018, 6, 3);
            SetEndDate(2019, 3, 13);

            //Set the cash for the strategy:
            SetCash("EUR",100000);


            //Define the symbol and "type" of our generic data:
            AddData<Interest>("SOYBN", Resolution.Daily);
            AddData<Interest>("USD", Resolution.Daily);
            var eurusd = QuantConnect.Symbol.Create("EURUSD", SecurityType.Forex, Market.Oanda);
            AddSecurity(SecurityType.Forex,eurusd, Resolution.Daily);
            soybn = QuantConnect.Symbol.Create("SOYBNUSD", SecurityType.Cfd, Market.Oanda);
            AddSecurity(SecurityType.Cfd,soybn,Resolution.Daily);
            SetWarmUp(TimeSpan.FromDays(10));
        }

        public override void OnData(Slice data)
        {
            if(IsWarmingUp)
            {
                return;
            }
            if (!Portfolio.Invested)
            {
                MarketOrder(soybn, 5000);
                Debug("Purchased Soy");
            }
        }

        //For a long position:
        //Financing Charges on Base = units * ({BASE}Interest Rate %) * (Time in years) * ({BASE}/Primary Currency)
        //Financing Charges on Quote = (converted units) * ({QUOTE} Interest Rate %) * (Time in years) * ({QUOTE}/Primary Currency)
        //Total Financing = Financing Charges on Base - Financing Charges on Quote
        //For a short position:
        //Financing Charges on Quote = (converted units) * ({QUOTE} Interest Rate %) * (Time in years) * ({QUOTE}/Primary Currency)
        //Financing Charges on Base = units * ({BASE} Interest Rate %) * (Time in years) * ({BASE}/Primary Currency)
        //Total Interest = Financing Charges on Quote - Financing Charges on Base

        private void CalculateInterest()
        {
            foreach (var security in Portfolio.Securities.Values)
            {
                if(security.Invested)
                {
                    var units = security.Holdings.AbsoluteQuantity;
                    Interest quotesymbol;
                    Interest basesymbol;

                    interestList.TryGetValue(security.QuoteCurrency.Symbol, out quotesymbol);
                    interestList.TryGetValue(security.Symbol.ID.Symbol.Replace(security.QuoteCurrency.Symbol, ""), out basesymbol);
                    if (basesymbol == null || quotesymbol == null)
                    {
                        return;
                    }
                    if (security.Holdings.Quantity>0)
                    {

                        var baseInterest = basesymbol.Bid; //borrow
                        var yearsecs = 365*24*60*60m;
                        var timeyears = (decimal)security.Exchange.Hours.RegularMarketDuration.TotalSeconds / yearsecs;
                        var tradePrice = security.Price;
                        var accountCurrency = 1/Portfolio.Securities["EURUSD"].Price; //convert usd to eur
                        var quoteInterest = quotesymbol.Ask; //lend
                        var value = units * timeyears * (baseInterest / 100) * tradePrice * accountCurrency;
                        var v2 = units * timeyears * (quoteInterest / 100) * tradePrice * accountCurrency;
                        var result = value - v2;
                        Debug(string.Format("{0} {1} {2} {3:N2}", basesymbol.Bid, quotesymbol.Ask, Time, result));
                        Portfolio.CashBook["EUR"].AddAmount(result);
                        //security.Holdings.AddNewFee(result);
                        Debug(string.Format("{0:N2}", Portfolio.CashBook["EUR"].Amount));
                    }
                    else
                    {
                        var baseInterest = basesymbol.Ask; //lend
                        var yearsecs = 365 * 24 * 60 * 60m;
                        var timeyears = (decimal)security.Exchange.Hours.RegularMarketDuration.TotalSeconds / yearsecs;
                        var tradePrice = security.Price;
                        var accountCurrency = 1 / Portfolio.Securities["EURUSD"].Price; //convert usd to eur
                        var quoteInterest = quotesymbol.Bid; //borrow
                        var value = units * timeyears * (baseInterest / 100) * tradePrice * accountCurrency;
                        var v2 = units * timeyears * (quoteInterest / 100) * tradePrice * accountCurrency;
                        var result = v2 - value;
                        Debug(string.Format("{0} {1} {2} {3:N2}", basesymbol.Ask, quotesymbol.Bid, Time, result));
                        Portfolio.CashBook["EUR"].AddAmount(result);
                        //security.Holdings.AddNewFee(result);
                        Debug(string.Format("{0:N2}", Portfolio.CashBook["EUR"].Amount));
                    }
                }
            }

        }


        /// <summary>
        /// OnData is the primary entry point for youm algorithm. New data is piped into your algorithm here
        /// via TradeBars objects.
        /// </summary>
        /// <param name="data">TradeBars IDictionary object</param>
        public void OnData(Interest data)
        {
            try
            {
                if(interestList.ContainsKey(data.Symbol.ID.Symbol))
                {
                    interestList.Remove(data.Symbol.ID.Symbol);
                }
                interestList.Add(data.Symbol.ID.Symbol, data);
          
                //Debug(string.Format("{0} {1} {2} {3} {4}", data.Symbol.ID.Symbol, data.Bid, data.Ask, data.DateTime, Time));
            }
            catch (Exception err)
            {
                Debug("Error: " + err.Message);
            }
        }

        /// <summary>
        /// End of a trading day event handler. This method is called at the end of the algorithm day (or multiple times if trading multiple assets).
        /// </summary>
        /// <remarks>Method is called 10 minutes before closing to allow user to close out position.</remarks>
        public override void OnEndOfDay()
        {
            CalculateInterest();
        }
    }

    /// <summary>
    /// Interest Custom Data Class
    /// </summary>
    public class Interest : BaseData
    {
        /// <summary>
        /// Bid Rate
        /// </summary>
        public decimal Bid;
        /// <summary>
        /// Ask Rate
        /// </summary>
        public decimal Ask;
        /// <summary>
        /// Instrument
        /// </summary>
        public string Instrument;
        /// <summary>
        /// DateTime
        /// </summary>
        public DateTime DateTime;

        /// <summary>
        /// Default initializer for Interest.
        /// </summary>
        public Interest()
        {
        }

        /// <summary>
        /// Return the URL string source of the file. This will be converted to a stream
        /// </summary>
        public override SubscriptionDataSource GetSource(SubscriptionDataConfig config, DateTime date, bool isLiveMode)
        {
            //"http://127.0.0.1:8080/interest.csv"

            return new SubscriptionDataSource("http://www.mocky.io/v2/5c8abc1b2e0000b732d64dc9", SubscriptionTransportMedium.RemoteFile);
        }

        /// <summary>
        /// Reader converts each line of the data source into BaseData objects. Each data type creates its own factory method, and returns a new instance of the object
        /// each time it is called.
        /// </summary>
        public override BaseData Reader(SubscriptionDataConfig config, string line, DateTime date, bool isLiveMode)
        {
            //New Interest object
            var interest = new Interest();

            try
            {
                //Example File Format:
                //CURRENCY,INSTRUMENT,BID,ASK,TEXTDATE,DATE,HOUR
                //Australia 200,AU200,4.1300,6.6800,Wed Dec 19 19:09:25 2018,20181219,19:09:25
                //Australia 200,AU200,1.0500,2.0000,Wed Dec 19 07:04:29 2018,20181207,07:04:29
                var data = line.Split(';');
                interest.Symbol = data[1];
                interest.Bid = Convert.ToDecimal(data[2], CultureInfo.InvariantCulture);
                interest.Ask = Convert.ToDecimal(data[3], CultureInfo.InvariantCulture);
                interest.DateTime = System.DateTime.ParseExact(string.Format("{0} {1} GMT", data[5], data[6]), "yyyyMMdd HH:mm:ss Z", CultureInfo.InvariantCulture);
                interest.Time = System.DateTime.ParseExact(string.Format("{0} {1} GMT", data[5], data[6]), "yyyyMMdd HH:mm:ss Z", CultureInfo.InvariantCulture);
                //interest.Time = System.DateTime.ParseExact(string.Format("{0} GMT", data[5]), "yyyyMMdd Z", CultureInfo.InvariantCulture);
                interest.Value = 1m;
            }
            catch
            {
            }
            return interest;
        }
    }

}