示例#1
0
bool OTOffer::MakeOffer(
    bool bBuyingOrSelling,      // True == SELLING, False == BUYING
    const int64_t& lPriceLimit, // Per Minimum Increment... (Zero price means
                                // it's a market order.)
    const int64_t& lTotalAssetsOffer, // Total assets available for sale or
                                      // purchase.
    const int64_t& lMinimumIncrement, // The minimum increment that must be
                                      // bought or sold for each transaction
    const int64_t& lTransactionNum,   // The transaction number authorizing this
                                      // trade.
    const time64_t& VALID_FROM,       // defaults to RIGHT NOW
    const time64_t& VALID_TO)         // defaults to 24 hours (a "Day Order")
{
    m_bSelling = bBuyingOrSelling; // Bid or Ask?
    SetTransactionNum(lTransactionNum);
    SetTotalAssetsOnOffer(lTotalAssetsOffer); // 500 bushels for sale.

    m_strContractType.Set((m_bSelling ? "ASK" : "BID"));

    // Make sure minimum increment isn't bigger than total Assets.
    // (If you pass them into this function as the same value, it's functionally
    // a "FILL OR KILL" order.)
    int64_t lRealMinInc = lMinimumIncrement;
    if (lMinimumIncrement > lTotalAssetsOffer) // Once the total, minus finish
                                               // so far, is smaller than the
                                               // minimum increment,
        lRealMinInc = lTotalAssetsOffer; // then the OTTrade object I am linked
                                         // to will expire and remove me from
                                         // the market.
    // OR it could set the minimum increment to the remainder. But then need to
    // calc price.

    SetMinimumIncrement(lRealMinInc); // Must sell in 50 bushel increments.
                                      // (Perhaps on the 10-bushel market it
                                      // will sell in 5 increments of 10.)
    SetPriceLimit(lPriceLimit);       // Won't sell for any less than $10 per
    // increment. (Always get best market price.)
    SetFinishedSoFar(0); // So far have already sold 350 bushels. Actual amount
                         // available is (total - finished).

    time64_t REAL_VALID_FROM = VALID_FROM;
    time64_t REAL_VALID_TO = VALID_TO;

    if (OT_TIME_ZERO >= VALID_FROM) {
        REAL_VALID_FROM =
            OTTimeGetCurrentTime(); // This time is set to TODAY NOW
    }

    if (OT_TIME_ZERO >= VALID_TO) {
        // (All offers default to a "DAY ORDER" if valid dates not specified.)
        REAL_VALID_TO = OTTimeAddTimeInterval(
            REAL_VALID_FROM,
            OTTimeGetSecondsFromTime(OT_TIME_DAY_IN_SECONDS)); // 1 day.
    }

    SetValidFrom(REAL_VALID_FROM);
    SetValidTo(REAL_VALID_TO);

    return true;
}
示例#2
0
// Verify whether the CURRENT date is WITHIN the VALID FROM / TO dates.
bool Instrument::VerifyCurrentDate()
{
    const time64_t CURRENT_TIME = OTTimeGetCurrentTime();

    if ((CURRENT_TIME >= m_VALID_FROM) &&
        ((CURRENT_TIME <= m_VALID_TO) || (OT_TIME_ZERO == m_VALID_TO)))
        return true;
    else
        return false;
}
示例#3
0
// This is called by the client side. First you call MakeOffer() to set up the
// Offer,
// then you call IssueTrade() and pass the Offer into it here.
bool OTTrade::IssueTrade(OTOffer& offer, char stopSign, int64_t stopPrice)
{
    // Make sure the Stop Sign is within parameters (0, '<', or '>')
    if ((stopSign == 0) || (stopSign == '<') || (stopSign == '>'))
        stopSign_ = stopSign;
    else {
        otErr << "Bad data in Stop Sign while issuing trade: " << stopSign
              << "\n";
        return false;
    }

    // Make sure, if this IS a Stop order, that the price is within parameters
    // and set.
    if ((stopSign_ == '<') || (stopSign_ == '>')) {
        if (0 >= stopPrice) {
            otErr << "Expected Stop Price for trade.\n";
            return false;
        }

        stopPrice_ = stopPrice;
    }

    tradesAlreadyDone_ = 0;

    SetCreationDate(
        OTTimeGetCurrentTime()); // This time is set to TODAY NOW  (OTCronItem)

    // Validate the Notary ID, Instrument Definition ID, Currency Type ID, and
    // Date Range.
    if ((GetNotaryID() != offer.GetNotaryID()) ||
        (GetCurrencyID() != offer.GetCurrencyID()) ||
        (GetInstrumentDefinitionID() != offer.GetInstrumentDefinitionID()) ||
        (offer.GetValidFrom() < OT_TIME_ZERO) ||
        (offer.GetValidTo() < offer.GetValidFrom())) {
        return false;
    }

    //    currencyTypeID_ // This is already set in the constructors of this
    // and the offer. (And compared.)
    //    currencyAcctID_ // This is already set in the constructor of this.

    // Set the (now validated) date range as per the Offer.
    SetValidFrom(offer.GetValidFrom());
    SetValidTo(offer.GetValidTo());

    // Get the transaction number from the Offer.
    SetTransactionNum(offer.GetTransactionNum());

    // Save a copy of the offer, in XML form, here on this Trade.
    String strOffer(offer);
    marketOffer_.Set(strOffer);

    return true;
}
示例#4
0
// Verify whether the CURRENT date is AFTER the the VALID TO date.
// Notice, this will return false, if the instrument is NOT YET VALID.
// You have to use VerifyCurrentDate() to make sure you're within the
// valid date range to use this instrument. But sometimes you only want
// to know if it's expired, regardless of whether it's valid yet. So this
// function answers that for you.
bool Instrument::IsExpired()
{
    const time64_t CURRENT_TIME = OTTimeGetCurrentTime();

    // If the current time is AFTER the valid-TO date,
    // AND the valid_to is a nonzero number (0 means "doesn't expire")
    // THEN return true (it's expired.)
    //
    if ((CURRENT_TIME >= m_VALID_TO) && (m_VALID_TO > OT_TIME_ZERO))
        return true;
    else
        return false;
}
// PayDividendVisitor::Trigger() is used in
// OTUnitDefinition::VisitAccountRecords()
// cppcheck-suppress unusedFunction
bool PayDividendVisitor::Trigger(
    const Account& theSharesAccount)  // theSharesAccount
                                      // is, say, a Pepsi
                                      // shares
// account.  Here, we'll send a dollars voucher
// to its owner.
{
    const std::int64_t lPayoutAmount =
        (theSharesAccount.GetBalance() * GetPayoutPerShare());

    if (lPayoutAmount <= 0) {
        otOut << "PayDividendVisitor::Trigger: nothing to pay, "
                 "since this account owns no shares. (Returning "
                 "true.)";
        return true;  // nothing to pay, since this account owns no shares.
                      // Success!
    }
    OT_ASSERT(false == GetNotaryID()->empty());
    const auto theNotaryID = GetNotaryID();
    OT_ASSERT(!GetPayoutUnitTypeId()->empty());
    const Identifier& payoutUnitTypeId_ = (Identifier::Factory());
    OT_ASSERT(!GetVoucherAcctID()->empty());
    const Identifier& theVoucherAcctID = (GetVoucherAcctID());
    Nym& theServerNym = const_cast<Nym&>(server_.GetServerNym());
    const auto theServerNymID = Identifier::Factory(theServerNym);
    const Identifier& RECIPIENT_ID = theSharesAccount.GetNymID();
    OT_ASSERT(!GetNymID()->empty());
    const Identifier& theSenderNymID = (GetNymID());
    OT_ASSERT(!GetMemo()->empty());
    const String& strMemo = (GetMemo());
    // Note: theSenderNymID is the originator of the Dividend Payout.
    // However, all the actual vouchers will be from "the server Nym" and
    // not from theSenderNymID. So then why is it even here? Because anytime
    // there's an error, the server will send to theSenderNymID instead of
    // RECIPIENT_ID (so the original sender can have his money back, instead of
    // just having it get lost in the ether.)
    bool bReturnValue = false;

    auto theVoucher{
        server_.API().Factory().Cheque(theNotaryID, Identifier::Factory())};

    OT_ASSERT(false != bool(theVoucher));

    // 10 minutes ==    600 Seconds
    // 1 hour    ==     3600 Seconds
    // 1 day    ==    86400 Seconds
    // 30 days    ==  2592000 Seconds
    // 3 months ==  7776000 Seconds
    // 6 months == 15552000 Seconds

    const time64_t VALID_FROM =
        OTTimeGetCurrentTime();  // This time is set to TODAY NOW
    const time64_t VALID_TO = OTTimeAddTimeInterval(
        VALID_FROM,
        OTTimeGetSecondsFromTime(OT_TIME_SIX_MONTHS_IN_SECONDS));  // This time
                                                                   // occurs in
    // 180 days (6 months).
    // Todo hardcoding.
    TransactionNumber lNewTransactionNumber = 0;
    auto context = server_.API().Wallet().mutable_ClientContext(
        theServerNym.ID(), theServerNym.ID());
    bool bGotNextTransNum =
        server_.GetTransactor().issueNextTransactionNumberToNym(
            context.It(), lNewTransactionNumber);  // We save the transaction
    // number on the server Nym (normally we'd discard it) because
    // when the cheque is deposited, the server nym, as the owner of
    // the voucher account, needs to verify the transaction # on the
    // cheque (to prevent double-spending of cheques.)
    if (bGotNextTransNum) {
        const bool bIssueVoucher = theVoucher->IssueCheque(
            lPayoutAmount,          // The amount of the cheque.
            lNewTransactionNumber,  // Requiring a transaction number prevents
                                    // double-spending of cheques.
            VALID_FROM,  // The expiration date (valid from/to dates) of the
                         // cheque
            VALID_TO,  // Vouchers are automatically starting today and lasting
                       // 6
                       // months.
            theVoucherAcctID,  // The asset account the cheque is drawn on.
            theServerNymID,    // Nym ID of the sender (in this case the server
                               // nym.)
            strMemo,  // Optional memo field. Includes item note and request
                      // memo.
            Identifier::Factory(RECIPIENT_ID));

        // All account crediting / debiting happens in the caller, in
        // server::Server.
        //    (AND it happens only ONCE, to cover ALL vouchers.)
        // Then in here, the voucher either gets send to the recipient, or if
        // error, sent back home to
        // the issuer Nym. (ALL the funds are removed, then the vouchers are
        // sent one way or the other.)
        // Any returned vouchers, obviously serve to notify the dividend payer
        // of where the errors were
        // (as well as give him the opportunity to get his money back.)
        //
        bool bSent = false;
        if (bIssueVoucher) {
            // All this does is set the voucher's internal contract string to
            // "VOUCHER" instead of "CHEQUE". We also set the server itself as
            // the remitter, which is unusual for vouchers, but necessary in the
            // case of dividends.
            //
            theVoucher->SetAsVoucher(theServerNymID, theVoucherAcctID);
            theVoucher->SignContract(theServerNym);
            theVoucher->SaveContract();

            // Send the voucher to the payments inbox of the recipient.
            //
            const auto strVoucher = String::Factory(*theVoucher);
            auto thePayment{server_.API().Factory().Payment(strVoucher)};

            OT_ASSERT(false != bool(thePayment));

            // calls DropMessageToNymbox
            bSent = server_.SendInstrumentToNym(
                theNotaryID,
                theServerNymID,  // sender nym
                RECIPIENT_ID,    // recipient nym
                *thePayment,
                "payDividend");    // todo: hardcoding.
            bReturnValue = bSent;  // <======= RETURN VALUE.
            if (bSent)
                m_lAmountPaidOut +=
                    lPayoutAmount;  // At the end of iterating all accounts, if
                                    // m_lAmountPaidOut is less than
            // lTotalPayoutAmount, then we return to rest
            // to the sender.
        } else {
            const auto strPayoutUnitTypeId = String::Factory(
                           Identifier::Factory(payoutUnitTypeId_)),
                       strRecipientNymID = String::Factory(RECIPIENT_ID);
            otErr << "PayDividendVisitor::Trigger: ERROR failed issuing "
                  << "voucher (to send to dividend payout recipient.) WAS "
                  << "TRYING TO PAY " << lPayoutAmount
                  << " of instrument definition " << strPayoutUnitTypeId
                  << " to Nym " << strRecipientNymID << ".\n";
        }
        // If we didn't send it, then we need to return the funds to where they
        // came from.
        //
        if (!bSent) {
            auto theReturnVoucher{server_.API().Factory().Cheque(
                theNotaryID, Identifier::Factory())};

            OT_ASSERT(false != bool(theReturnVoucher));

            const bool bIssueReturnVoucher = theReturnVoucher->IssueCheque(
                lPayoutAmount,          // The amount of the cheque.
                lNewTransactionNumber,  // Requiring a transaction number
                                        // prevents double-spending of cheques.
                VALID_FROM,  // The expiration date (valid from/to dates) of the
                             // cheque
                VALID_TO,    // Vouchers are automatically starting today and
                             // lasting 6 months.
                theVoucherAcctID,  // The asset account the cheque is drawn on.
                theServerNymID,    // Nym ID of the sender (in this case the
                                   // server nym.)
                strMemo,  // Optional memo field. Includes item note and request
                          // memo.
                Identifier::Factory(theSenderNymID));  // We're returning the
                                                       // money to its original
                                                       // sender.
            if (bIssueReturnVoucher) {
                // All this does is set the voucher's internal contract string
                // to
                // "VOUCHER" instead of "CHEQUE".
                //
                theReturnVoucher->SetAsVoucher(
                    theServerNymID, theVoucherAcctID);
                theReturnVoucher->SignContract(theServerNym);
                theReturnVoucher->SaveContract();

                // Return the voucher back to the payments inbox of the original
                // sender.
                //
                const auto strReturnVoucher =
                    String::Factory(*theReturnVoucher);
                auto theReturnPayment{
                    server_.API().Factory().Payment(strReturnVoucher)};

                OT_ASSERT(false != bool(theReturnPayment));

                // calls DropMessageToNymbox
                bSent = server_.SendInstrumentToNym(
                    theNotaryID,
                    theServerNymID,  // sender nym
                    theSenderNymID,  // recipient nym (original sender.)
                    *theReturnPayment,
                    "payDividend");  // todo: hardcoding.
                if (bSent)
                    m_lAmountReturned +=
                        lPayoutAmount;  // At the end of iterating all accounts,
                                        // if m_lAmountPaidOut+m_lAmountReturned
                                        // is less than lTotalPayoutAmount, then
                                        // we return the rest to the sender.
            } else {
                const auto strPayoutUnitTypeId =
                               String::Factory(payoutUnitTypeId_),
                           strSenderNymID = String::Factory(theSenderNymID);
                otErr << "PayDividendVisitor::Trigger: ERROR "
                         "failed issuing voucher (to return back to "
                         "the dividend payout initiator, after a failed "
                         "payment attempt to the originally intended "
                         "recipient.) WAS TRYING TO PAY "
                      << lPayoutAmount << " of instrument definition "
                      << strPayoutUnitTypeId << " to Nym " << strSenderNymID
                      << ".\n";
            }
        }   // if !bSent
    } else  // !bGotNextTransNum
    {
        const auto strPayoutUnitTypeId = String::Factory(payoutUnitTypeId_),
                   strRecipientNymID = String::Factory(RECIPIENT_ID);
        otErr << OT_METHOD << __FUNCTION__
              << ": ERROR!! Failed issuing next transaction number while "
              << "trying to send a voucher (while paying dividends.) "
              << "WAS TRYING TO PAY " << lPayoutAmount
              << " of instrument definition " << strPayoutUnitTypeId->Get()
              << " to Nym " << strRecipientNymID->Get() << ".\n";
    }

    return bReturnValue;
}
示例#6
0
// OTCron calls this regularly, which is my chance to expire, etc.
// Return True if I should stay on the Cron list for more processing.
// Return False if I should be removed and deleted.
bool OTTrade::ProcessCron()
{
    // Right now Cron is called 10 times per second.
    // I'm going to slow down all trades so they are once every
    // GetProcessInterval()
    if (GetLastProcessDate() > OT_TIME_ZERO) {
        // (Default ProcessInterval is 1 second, but Trades will use 10 seconds,
        // and Payment Plans will use an hour or day.)
        if (OTTimeGetTimeInterval(OTTimeGetCurrentTime(),
                                  GetLastProcessDate()) <= GetProcessInterval())
            return true;
    }
    
    // Keep a record of the last time this was processed.
    // (NOT saved to storage, only used while the software is running.)
    // (Thus no need to release signatures, sign contract, save contract, etc.)
    SetLastProcessDate(OTTimeGetCurrentTime());

    // PAST END DATE?
    // First call the parent's version (which this overrides) so it has
    // a chance to check its stuff. Currently it checks IsExpired().
    if (!ot_super::ProcessCron())
        return false; // It's expired or flagged for removal--remove it from
                      // Cron.

    // You might ask, why not check here if this trade is flagged for removal?
    // Supposedly the answer is, because it's only below that I have the market
    // pointer,
    // and am able to remove the corresponding trade from the market.
    // Therefore I am adding a hook for "onRemoval" so that Objects such as
    // OTTrade ALWAYS
    // have the opportunity to perform such cleanup, without having to juggle
    // such logic.

    // REACHED START DATE?
    // Okay, so it's not expired. But might not have reached START DATE yet...
    if (!VerifyCurrentDate())
        return true; // The Trade is not yet valid, so we return. BUT, we return
                     //  true, so it will stay on Cron until it BECOMES valid.

    // TRADE-specific stuff below.

    bool bStayOnMarket =
        true; // by default stay on the market (until some rule expires me.)

    Identifier OFFER_MARKET_ID;
    OTMarket* market = nullptr;

    // If the Offer is already active on a market, then I already have a pointer
    // to
    // it. This function returns that pointer. If nullptr, it tries to find the
    // offer on
    // the market and then sets the pointer and returns. If it can't find it, IT
    // TRIES
    // TO ADD IT TO THE MARKET and sets the pointer and returns it.
    OTOffer* offer = GetOffer(
        &OFFER_MARKET_ID, &market); // Both of these parameters are optional.

    // In this case, the offer is NOT on the market.
    // Perhaps it wasn't ready to activate yet.
    if (offer == nullptr) {
        
        // The offer SHOULD HAVE been on the market, since we're within the
        // valid range,
        // and GetOffer adds it when it's not already there.

        //        otErr << "OTTrade::ProcessCron: Offer SHOULD have been on
        // Market. I might ASSERT this.\n"; // comment this out

        // Actually! If it's a Stop Order, then it WOULD be within the valid
        // range, yet would
        // not yet have activated. So I don't want to log some big error every
        // time a stop order
        // checks its prices.
    }
    else if (market == nullptr) {
        
        // todo. (This will already leave a log above in GetOffer somewhere.)
        //        otErr << "OTTrade::ProcessCron: Market was nullptr.\n"; //
        // comment this out
    }
    else // If a valid pointer was returned, that means the offer is on the
           // market.
    {
        // Make sure it hasn't already been flagged by someone else...
        if (IsFlaggedForRemoval()) // This is checked above in
                                   // OTCronItem::ProcessCron().
            bStayOnMarket = false; // I'm leaving the check here in case the
                                   // flag was set since then.

        else // Process it!  <===================
        {
            otInfo << "Processing trade: " << GetTransactionNum() << ".\n";

            bStayOnMarket = market->ProcessTrade(*this, *offer);
            // No need to save the Trade or Offer, since they will
            // be saved inside this call if they are changed.
        }
    }

    // Return True if I should stay on the Cron list for more processing.
    // Return False if I should be removed and deleted.
    return bStayOnMarket; // defaults true, so if false, that means someone is
                          // removing it for a reason.
}